扯淡
这个游戏让我想起了小学和同学玩"抓人",有过游戏经验的同学都知道,我们要找一个地方,可以和"追捕者"一直转圈圈,比如有东西两个楼道的教学楼就是不错的地方。
题意简化
现在有两个人小(L)(loidc)和小(C)(cony),都处于一张无向联通图上的某个节点(给出他们所在的节点)而且这张图不存在三元环,每次一个人每回合可以走一步,小(L)尽量避免与小(C)相遇,而小(C)则尽量想让两个人相遇(有没有一点点像狗血的某种故事.......)两个人都选择最佳策略,问最终会不会相遇,如果相遇,那么最少用几回合?。
解题思路
你会说,我一开头说的是废话,但是事实则不然.......这道题和上面的抓人游戏类似。
抽象到题目里面,什么地方是可以和"追捕者"一直转圈圈的?不难想,是环上面。一开始我很费解,为什么给出这张图不含三元环,后来我思考了一下,三元环其实并不能满足一直转圈圈的性质,题解中的第一篇就讲到了这一点。
那最优策略就是小(L)就要赶紧跑到环上面,小(C)要赶紧堵住小(L),阻止小(C)到环上面。
小(L)为了尽快跑到环上面,他选择跑到一个环上面的点(但是并非是距离自己最近的,想一想为什么),假设这个点为(P),那么小(L)怎么办?显然小(L)被迫的也要跑到(P)点,不然怎么阻止小(L)上到环上面去?所以解法逐渐浮现在我们眼前。
——————————————————————分割线——————————————————————
上面的是对于题目的分析,要是你已经分析过了可以直接看下面的做法
首先我们要找环啊,于是(tarjan)是首选,然后我们要确定“尽快跑到环上的距离小(L)最近的一点所用的时间”,于是我们需要(Dijkstra)最短路算法,计算一下小(L)到所有点的最短距离,然后还需要计算小(C)到每一个点的最短距离,然后枚举每一个点,先判断其是否在一个环上面,然后判断一下小(L)到达它的时间是否小于小(C)到达它的时间,如果存在任意一个这样的点,输出"No"。
如果不存在这个点怎么办?
可以把小(L)和小(C)的行走过程分为(x)个回合,先让小(L)走(x)个回合,那么他最终会在一个点停下来。无论这个点是哪个点,实际上只需要计算小(C)到这个点的最短路径即可,同时请想,假若小(L)到达这个点所用的时间大于小(C)到这个点的时间,那小(L)显然无法到达这个点,所以最后的答案会是满足距离小(C)最远的且满足小(L)到这个点的距离小于小(C)到这个点的距离的点。
————————————————————end————————————————————
以上就是关于这道题的做法!
贴出代码
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
const int MAXN = 6000, MAXM = 30005;
int n,m,s,t,tot = 0,cnt = 0,now = 0,z = 0;
int dfn[MAXN],low[MAXN],siz[MAXN],color[MAXN];
int tack[MAXN],vis[MAXN],book[MAXN];//tarjan算法缩点常用数组
int start[MAXN],tag[MAXN],dis[MAXN],D1[MAXN],D2[MAXN];
//D1为小L到各点的距离,D2为小C到各点的距离,tag表示是否在环上
struct Edge {
int next,to;
} edge[MAXM];
struct data {
int sum,id;
bool operator <(const data& fz) const {
return fz.sum < sum;
}
};
priority_queue <data> q;//上面是Dijkstra要用到的东西
void add(int from,int to);
void tarjan(int x,int fa);
void Dijkstra(int S);
int main()
{
cin >> n >> m >> s >> t;
for(int i = 1 ; i <= m ; i ++)
{
int u, v;
cin >> u >> v;
add(u,v);
add(v,u);
}
for(int i = 1 ; i <= n ; i ++)
if(!dfn[i])tarjan(i,i);//tarjan
for(int i = 1 ; i <= n ; i ++)
if(siz[color[i]] > 1)tag[i] = 1;//一个点单独成环没用...
Dijkstra(s);
for(int i = 1 ; i <= n ; i ++)D1[i] = dis[i];
Dijkstra(t);//跑两遍Dijkstra
int M = -1000000;
for(int i = 1 ; i <= n ; i ++)D2[i] = dis[i];
int flag = 0;
for(int i = 1 ; i <= n ; i ++)
if(D1[i] < D2[i])
{
M = max(M,D2[i]);
if(tag[i] )flag = 1;
}
if(flag == 1)cout <<"NO
";
else cout << M << endl;
return 0;
}
void add(int from,int to)
{
cnt ++;
edge[cnt].to = to;
edge[cnt].next = start[from];
start[from] = cnt;
return ;//前向星加边
}
void tarjan(int x,int fa)//标准的tarjan即可
{
dfn[x] = low[x] = ++now;
tot ++ , vis[tot] = x;
book[x] = 1;
for(int i = start[x] ; i ; i = edge[i].next)
{
int to = edge[i].to;
if(to == fa)continue;//无向边,防止反复横跳
if(!dfn[to])
{
tarjan(to,x);
low[x] = min(low[x],low[to]);
}
else if(book[to]) low[x] = min(low[x],dfn[to]);
}
if(dfn[x] == low[x])
{
z ++;
while(vis[tot + 1] != x && tot > 0)
{
color[vis[tot]] = z;
siz[z] ++;
book[tot] = 0;
tot --;
}
}
return ;
}
void Dijkstra(int S)//Dijkstra求最短路即可
{
for(int i = 1 ; i <= n ; i ++)dis[i] = INF;
dis[S] = 0;
q.push(data {dis[S],S});
memset(book,0,sizeof(book));
book[S] = 1;
while(!q.empty())
{
data X = q.top();
q.pop();
if(X.sum != dis[X.id])continue;
book[X.id] = 1;
int x = X.id;
for(int i = start[x] ; i ; i = edge[i].next)
{
int to = edge[i].to;
if(dis[to] > dis[x] + 1)
{
dis[to] = dis[x] + 1;
if(!book[to])q.push(data {dis[to],to});
}
}
}
return ;
}