[Luogu]P2302 loidc,跑起来

[Luogu]P2302 loidc,跑起来

扯淡

这个游戏让我想起了小学和同学玩"抓人",有过游戏经验的同学都知道,我们要找一个地方,可以和"追捕者"一直转圈圈,比如有东西两个楼道的教学楼就是不错的地方。

题意简化

现在有两个人小(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 ;
}

这个题目没有很多的细节,但是注意写对基础算法!

原文地址:https://www.cnblogs.com/MYCui/p/13916652.html