POI 1998 追趕 Chase
這道題的解決方法隱藏得很深,不經過嚴密的數學推理分析是很難想到正確方法的。
注意到題上的一個看似無關緊要的條件,“不包括三角形”,這是一個突破口。由這個條件,我們可以證明,如果一個圖不存在度數爲1的頂點,B永遠也追不上A。也就是B想追上A,必須讓A“走投無路”。
於是我們首先把原圖處理一下,求出對於A來說的安全區。對於A來說的安全區,也就是一個沒有度爲1的頂點的最大子圖。我們把這個安全區求出,並標記上。
A要想不被B抓住,則一定要向安全區中逃跑。如果A能夠在B追上A之前逃離到安全區,則B就永遠也追不上A。否則,A無論如何也會被B追上。在A“必死無疑”的時候,A要想盡可能晚的被B追上,就必須想遠離B的方向跑。
有了以上的分析,得出下列算法 1、求出安全區的頂點。 2、分別求出A,B初始位置開始,到每個頂點的距離,記作DA[i]和DB[i]。 3、若存在安全區中的頂點k,使得DA[k]<DB[k],則B追不上A,輸出"NIE",結束。 4、如果不存在上述頂點k,則找到滿足DA[i]<DB[i]時,DB[i]的最大值,輸出DB[i]的最大值。
時間複雜度:O(M+N)
/*
* Problem: POI1998 gon
* Author: Guo Jiabao
* Time: 2008.12.3 18:42:14
* State: Solved
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
class tList
{
public:
tList *next;
int v;
tList(int tv) : v(tv)
{
next=0;
}
};
class tQueue
{
public:
tList *first,*last;
int size;
tQueue()
{
reset();
}
void reset()
{
first=last=0;
size=0;
}
void ins(int v)
{
if (first)
last=last->next=new tList(v);
else
first=last=new tList(v);
size++;
}
int pop()
{
int rtn=first->v;
tList *t=first;
size--;
first=first->next;
delete t;
return rtn;
}
};
const int MAXN=3001;
int N,M,A,B,Ans;
int Sf[MAXN],Degree[MAXN];
int DA[MAXN],DB[MAXN];
tQueue adjl[MAXN],Q;
void init()
{
int i,a,b;
freopen("gon.in","r",stdin);
freopen("gon.out","w",stdout);
scanf("%d%d%d%d",&N,&M,&A,&B);
for (i=1;i<=M;i++)
{
scanf("%d%d",&a,&b);
adjl[a].ins(b);
adjl[b].ins(a);
Degree[a]++;
Degree[b]++;
}
for (i=1;i<=N;i++)
{
Sf[i]=true;
DA[i]=DB[i]=-1;
}
}
void safe()
{
int i,j;
tList *k;
for (i=1;i<=N;i++)
{
if (Degree[i]==1)
{
Q.ins(i);
}
}
while (Q.size)
{
i=Q.pop();
Degree[i]=0;
Sf[i]=false;
for (k=adjl[i].first;k;k=k->next)
{
j=k->v;
Degree[j]--;
if (Degree[j]==1)
Q.ins(j);
}
}
}
void dist()
{
int i,j;
tList *k;
Q.reset();
Q.ins(A);
DA[A]=0;
while (Q.size)
{
i=Q.pop();
for (k=adjl[i].first;k;k=k->next)
{
j=k->v;
if (DA[j]==-1)
{
DA[j]=DA[i]+1;
Q.ins(j);
}
}
}
Q.reset();
Q.ins(B);
DB[B]=0;
while (Q.size)
{
i=Q.pop();
for (k=adjl[i].first;k;k=k->next)
{
j=k->v;
if (DB[j]==-1)
{
DB[j]=DB[i]+1;
Q.ins(j);
}
}
}
}
void solve()
{
int k;
for (k=1;k<=N;k++)
{
if (Sf[k] && DA[k] < DB[k])
{
printf("NIE");
return;
}
if (DA[k] < DB[k] && DB[k] > Ans)
{
Ans=DB[k];
}
}
printf("%d",Ans);
}
int main()
{
init();
safe();
dist();
solve();
return 0;
}
<h2><span class="mw-headline">追趕</span></h2>
“追趕”是兩個人用棋盤玩的遊戲。棋盤由編號由1到n的方塊組成。每兩個不同的方塊將被得知它們是否與另一個相鄰。每一個玩家都有一個由他控制的硬 幣。在遊戲的開始,玩家的硬幣被放在固定的不同的方塊中。一個回合內一個玩家可以不移動他的硬幣,也可以把硬幣移到一個相鄰的方塊裏。
棋盤具有如下屬性:
它不包括三角形,也就是沒有任意三個不同的方塊,它們兩兩相鄰, 每一個玩家到達都能到達任意一個方塊。
一個遊戲由許多回合組成。在一個回合內,每一個玩家移一步。每一個回合由玩家A開始。我們說如果兩個硬幣在同一個方塊中,那麼玩家A被玩家 B趕上。問題要求,決定是否能對於給定一個硬幣的初始位置,玩家B在對手獨立移動的情況下都能趕上玩家A。如果這樣,在兩個人都玩得很好條件下, 多少回合玩家B趕上玩家A(即,玩家A儘量遠離玩家B,玩家B儘可能快的趕上有些者A)。
例如
<a class="image" title="Image:Gon.png" href="http://www.ruvtex.cn/wiki/Image:Gon.png"><img src="http://www.ruvtex.cn/mw/images/9/91/Gon.png" alt="Image:Gon.png" width="208" height="172" /></a>
用圖表示棋盤,相鄰的方塊(用圓圈表示)用邊相連接. 如果在遊戲的開始玩家A 和 B分別放置硬幣在9和4的位置,那麼在第三輪(如果兩個人都玩的很好),玩家B將趕上玩家A. 如果開始玩家A 和 B分別放置硬幣在8和4的位置,那麼玩家B將不能趕上玩家A(如果A不出錯).
任務
寫一個程序:
- 文本文件GON.IN t中描述了一個棋盤,初始狀態下方塊中放置硬幣的編號。
- 判斷是否玩家B能趕上玩家A, 如果能,計算將有需要多少回合(如果兩個玩家都玩得很好);
將結果寫入文本文件 GON.OUT中.
輸入
在文件GON.IN 的第一行有4個整數n, m, a 和b,它們用單個空格分隔。2<=n<=3000, -1<=m<=15000, 1<=a,b<=n ,a <b。它們分別表示棋盤中的方塊數目, 相鄰的一對方塊(無序)數目,玩家A的硬幣放置在方塊中的編號,玩家B的硬幣放置在方塊中的編號。下面的每一行包含兩個用一個空格分開的整數,表示每一對 相鄰的方塊編號。
輸出
在文本文件GON.OUT 的第一行寫入如下信息:
一個單詞NIE ,如果玩家 B不能趕上玩家A, 或者
一個整數-如果玩家B能趕上玩家A的最少遊戲輪數
輸入示例
9 11 9 4 1 2 3 2 1 4 4 7 7 5 5 1 6 9 8 5 9 8 5 3 4 8
輸出示例 3