AHOI2005 穿越磁場

這道題道德解法是離散化+染色+BFS最短路徑。首先,由於座標範圍很大,離散化處理是很有必要的,這樣可以把座標範圍從10000壓縮到210以內。在離散化以後的格子中進行一次Floodfill,對每個封閉區域進行染色處理。接下來,把每個染色區域看成圖中的一個頂點,相鄰的染色區域建立一條權值爲1的無向邊。最後,求S到T所在染色區域對應頂點之間的最短路徑,由於邊權全部爲1,只需一邊BFS即可。

這道題是大名鼎鼎的《騙分導論》上的一道例題,除了騙分以外,這種解法也是非常好的。

/* 
 * Problem: AHOI2005 cross
 * Author: Guo Jiabao
 * Time: 2009.4.17 10:32
 * State: Solved
 * Memo: 離散化 + 染色 + BFS
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
const int MAXN=101,MAXL=405,INF=0x7FFFFFF,MAXC=MAXN*MAXN;
const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
using namespace std;
struct Queue
{
    int Q[MAXC],head,tail,size;
    Queue() {head=size=0;tail=-1;}
    void ins(int p)
    {
        if (++tail>=MAXC) tail=0;
        Q[tail]=p;
        size++;
    }
    int pop()
    {
        int r=Q[head];
        if (++head>=MAXC) head=0;
        size--;
        return r;
    }
}Q;
struct edge
{
    edge *next;
    int t;
}*V[MAXC];
struct point
{
    int x,y;
}S,T,Rect[MAXN][2],*P[MAXN*2+3],MaxP;
int N,CP,Color,Start,Target,Ans;
int TK[MAXN*2+3];
int Bel[MAXL][MAXL];
int Dist[MAXC];
void init()
{
    int i,x,y,c;
    freopen("cross.in","r",stdin);
    freopen("cross.out","w",stdout);
    scanf("%d",&N);
    for (i=1;i<=N;i++)
    {
        scanf("%d%d%d",&x,&y,&c);
        Rect[i][0].x=x;Rect[i][0].y=y;
        Rect[i][1].x=x+c;Rect[i][1].y=y+c;
        P[++CP]=&Rect[i][0]; P[++CP]=&Rect[i][1];
    }
    scanf("%d%d%d%d",&S.x,&S.y,&T.x,&T.y);
    P[++CP]=&S; P[++CP]=&T;
}
inline int cmpx(const void *a,const void *b)
{
    point *A=*(point **)a, *B=*(point **)b;
    return A->x - B->x;
}
inline int cmpy(const void *a,const void *b)
{
    point *A=*(point **)a, *B=*(point **)b;
    return A->y - B->y;
}
void dispose()
{
    int i;point PT={-100000,-100000};
    MaxP.x=MaxP.y=0;
    P[0]=&PT;TK[0]=0;
    qsort(P+1,CP,sizeof(P[0]),cmpx);
    for (i=1;i<=CP;i++)
    {
        TK[i]=TK[i-1];
        if (P[i]->x!=P[i-1]->x)
            TK[i]++;
        if (TK[i] > MaxP.x)
            MaxP.x=TK[i];
    }
    for (i=1;i<=CP;i++)
        P[i]->x=TK[i];
    qsort(P+1,CP,sizeof(P[0]),cmpy);
    for (i=1;i<=CP;i++)
    {
        TK[i]=TK[i-1];
        if (P[i]->y!=P[i-1]->y)
            TK[i]++;
        if (TK[i] > MaxP.y)
            MaxP.y=TK[i];
    }
    for (i=1;i<=CP;i++)
        P[i]->y=TK[i];
}
inline bool inrange(int x,int y)
{
    return x>=0 && y>=0 && x<=MaxP.x+1 && y<=MaxP.y+1;
}
inline bool inrect(int x,int y,int k)
{
    return x>=Rect[k][0].x && x<Rect[k][1].x && y>=Rect[k][0].y && y<Rect[k][1].y;
}
bool cross(int x1,int y1,int x2,int y2)
{
    int i;
    for (i=1;i<=N;i++)
    {
        if (inrect(x1,y1,i) ^ inrect(x2,y2,i))
            return true;
    }
    return false;
}
void dfscolor(int x,int y)
{
    int k,tx,ty;
    Bel[x][y]=Color;
    for (k=0;k<4;k++)
    {
        tx=x+dx[k]; ty=y+dy[k];
        if (inrange(tx,ty) && !Bel[tx][ty] && !cross(x,y,tx,ty))
        {
            dfscolor(tx,ty);
        }
    }
}
void floodfill()
{
    int x,y;
    for (x=1;x<MaxP.x;x++)
    {
        for (y=1;y<MaxP.y;y++)
        {
            if (!Bel[x][y])
            {
                Color++;
                dfscolor(x,y);
            }
        }
    }
}
inline void addedge(int a,int b)
{
    edge e={V[a],b};
    V[a]=new edge(e);
}
void MakeGraph()
{
    int x,y,tx,ty,k,a,b;
    for (x=1;x<=MaxP.x;x++)
    {
        for (y=1;y<=MaxP.y;y++)
        {
            a=Bel[x][y];
            if (x==S.x && y==S.y)
                Start=a;
            if (x==T.x && y==T.y)
                Target=a;
            for (k=0;k<4;k++)
            {
                tx=x+dx[k]; ty=y+dy[k];
                if (inrange(tx,ty))
                {
                    b=Bel[tx][ty];
                    addedge(a,b);
                }
            }
        }
    }
}
void BFS()
{
    int i,j;
    memset(Dist,-1,sizeof(Dist));
    Q.ins(Start);
    Dist[Start]=0;
    while (Q.size)
    {
        i=Q.pop();
        for (edge *e=V[i];e;e=e->next)
        {
            j=e->t;
            if (Dist[j]==-1)
            {
                Dist[j]=Dist[i]+1;
                if (j==Target)
                    return;
                Q.ins(j);
            }
        }
    }
}
void solve()
{
    dispose();
    floodfill();
    MakeGraph();
    BFS();
    Ans=Dist[Target];
}
int main()
{
    init();
    solve();
    printf("%d\n",Ans);
    return 0;
}
<div class="MainText">

<a href="http://www.ruvtex.cn/cogs/problem/pdetail.php?pid=316" target="_blank">http://www.ruvtex.cn/cogs/problem/pdetail.php?pid=316</a>

<strong>穿越磁場</strong>

探險機器人在Samuel星球上尋找一塊奇特的礦石,然而此時它陷入了一片神祕的磁場區域,動彈不得。

探險空間站立刻掃描了這片區域,繪製出該區域的磁場分佈平面圖。這片區域中分佈了N個磁場,每個磁場呈正方形,且邊與座標軸平行。

例如下圖中,存在3個磁場,白點表示機器人的位置,黑點表示礦石的位置:

<span class="image"><a href="http://192.168.1.253/mw/images/e/ea/Cross_1.gif"><img src="http://192.168.1.253/mw/images/e/ea/Cross_1.gif" alt="Image:Cross 1.gif" width="355" height="192" /></a></span>

科學家們分析平面圖,進一步發現:這些磁場爲大小不一的正方形,可能相交,甚至覆蓋,但是它們的邊緣不會重合,頂點也不會重合。

例如下面的兩種情形是不會出現的:

<a href="http://192.168.1.253/mw/images/c/ca/Cross_2.gif"><img src="http://192.168.1.253/mw/images/c/ca/Cross_2.gif" alt="Image:Cross 2.gif" width="375" height="122" /></a>

科學家們給探險機器人啓動了磁力罩,這樣它就可以在磁場中自由穿越了。

初始時,探險機器人和所有礦石都不在任何磁場的邊緣。由於技術限制,在穿越過程中機器人只能夠水平或垂直移動,且不能夠沿着磁場的邊緣行動。

由於磁力罩的能量有限,科學家們希望探險機器人穿越儘量少的磁場邊緣採集到這塊礦石。例如上圖中,探險機器人最少需要穿越兩次磁場邊緣。

現在小聯請你編寫程序,幫助科學家們設計探險機器人的路線,統計探險機器人最少需要穿越多少次磁場邊緣。
輸入:第一行有一個整數N,表示有N個磁場(1 &lt; N &lt; 100)。隨後有N行,每行有三個整數X、Y、C(0 &lt; X ,Y ,C &lt; 10000),表示一個磁場左下角座標爲(X,Y),邊長爲C。接下來有一行,共有四個整數SX, SY, TX, TY,表示機器人初始座標爲(SX, SY),礦石座標爲(TX,TY)(其中,1 &lt; SX, SY, TX, TY &lt; 10000)。
輸出:單行輸出一個整數,表示機器人最少需要穿越多少次磁場邊緣。
樣例:

輸入:
<pre>2
1 3 3
2 1 4
0 0 3 4</pre>
輸出:
<pre>2</pre>
</div>

相關日誌