NKOI 3081 追赶游戏

28 篇文章 0 订阅

追赶游戏(chase.cpp/in/out) 1S/128M

追赶是两个人在木板上玩的游戏。板子由从1n编号的格子组成。每对不同的格子,要么相邻,要么不相邻。每位游戏者有一枚棋子。开始时,每游戏者的棋子放在不同的格子每一步,游戏者可以保持他的棋子原来位置不动,或者移动到一个相邻的格子上。

    游戏板有下列特征:

    1.它不含三角形,即不存在三个不同的格子满足每两个都是相邻的。

    2.每个格子有可能被每个游戏者走到。

一场游戏是由许多轮组成。一轮中每位游戏者最多能走一步。每轮都是由游戏者A开始。当某一轮时,两个棋子在同一个格子上时,那么我们说游戏者B抓住了游戏者A

如果两个游戏者都按最优的策略行动,即游戏者A尽可能逃得远,而游戏者B尽可能快地追赶游戏者A,那么对于给定的初始位置,游戏者B少需要多少轮才能够抓住游戏者A。例如,对于下图的游戏板


相邻的格子(由圆表示)由边相连。如果在游戏开始时,游戏者AB的棋子各自处于编号为94的格子上,那么在第3轮时游戏者B能抓住游戏者A(如果两个游戏者都按最优策略走)。如果游戏开始时,棋子分别位于编号为8(游戏者A),和编号为4(游戏者B)上,那么游戏者B不能抓住游戏者A(如果A走得正确)

输入格式:

1行:4个整数n,m,a,b(2n3000n-1m150001a,bn并且a<b)n表示板子上的格子数量,m表示多少对相邻格子,a表示游戏者A的棋子所放格子的编号,b表示游戏者B的棋子所放格子的编号。

下来m行,每行2整数,表示一相邻格子的编号。

 

输出格式

如果游戏者B不能抓住游戏者A,输出-1,否则输出一个整数,表示游戏者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


开始很容易想成博弈,后来发现不是,居然是图论

仔细分析后我们发现,如果A在被B抓住之前到了图中的一个双连通分量里,他就永远不会被抓到,因为即使B挡住了其中一条去路,A仍然可以选择另外一条路走开

那么我们怎么求图中双联通分量呢?很明显应该用到Tajan

用dis[0][i]表示A号点到i号点距离,dis[1][i]表示B号点到i号点距离,不难想到,对于一个双联通分量内的点k,如果有dis[0][k]<dis[1][k],那么输出-1

那么剩下的就是A一定会被追到的情况,这个时候A只能尽量跑远一点,在被B追上之前到达一个让B跑的最远的点,如果这个点为k,则也满足条件

dis[0][k]<dis[1][k]

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn=3005,maxm=30005,inf=0x3f3f3f3f;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int last[maxn],low[maxn],dfn[maxn],Belong[maxn],cnt[maxn],dis[2][maxn];
int scc,n,m,A,B,vistime;
bool isbri[maxm],vis[maxn],to[maxn];
struct node{
	int a,b,Next;
	node(int a,int b,int Next):a(a),b(b),Next(Next){}
};
vector<node>s;
void insert(int a,int b){
	s.push_back(node(a,b,last[a]));
	last[a]=s.size()-1;
	s.push_back(node(b,a,last[b]));
	last[b]=s.size()-1;
}
void spfa(int op){
	int t=(op==B),i;
	queue<int>q;
	memset(vis,0,sizeof(vis));
	for(i=1;i<=n;i++)dis[t][i]=inf;
	dis[t][op]=0,vis[op]=1;
	q.push(op);
	while(q.size()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(i=last[x];i>=0;i=s[i].Next){
			int v=s[i].b;
			if(dis[t][v]>dis[t][x]+1){
				dis[t][v]=dis[t][x]+1;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}
void tajan(int u,int fa){
	dfn[u]=low[u]=++vistime;
	int i,j,v;
	for(i=last[u];i>=0;i=s[i].Next){
		int v=s[i].b;
		if(dfn[v]==0){
			tajan(v,u);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])isbri[i]=1,isbri[i^1]=1;
		}
		else if(v!=fa)low[u]=min(low[u],dfn[v]);
	}
}
void dfs(int u){
	int i,v;
	cnt[scc]++;
	Belong[u]=scc;
	for(i=last[u];i>=0;i=s[i].Next){
		if(isbri[i])continue;
		v=s[i].b;
		if(Belong[v])continue;
		dfs(v);
	}
}
int main(){
	memset(last,-1,sizeof(last));
	int i,j,x,y,ans=0,id,curdis=inf;
	_read(n);_read(m);_read(A);_read(B);
	for(i=1;i<=m;i++){
		_read(x);_read(y);
		insert(x,y);
	}
	tajan(1,0);
	for(i=1;i<=n;i++)
	    if(!Belong[i]){
	    	scc++;
	    	dfs(i);
		}
	spfa(A);spfa(B);
	for(i=1;i<=n;i++)
		if(cnt[Belong[i]]>1){
			if(dis[0][i]<dis[1][i]){
				puts("-1");
				return 0;
			}
		}
		else if(dis[0][i]<dis[1][i])ans=max(ans,dis[1][i]);
	cout<<ans;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值