牛客多校十 - Shannon Switching Game (倒推,bfs)

https://ac.nowcoder.com/acm/contest/33195/F

题意
给定一个 n 个点,m 条边的无向图,可能存在重边,不存在自环。
有一个棋子放在起点 s t st st,要走到终点 e n en en

两个人轮流操作,第二个人先操作:

  • 第一个人选择棋子相邻的一条边,然后删掉;
  • 第二个人选择棋子相邻的一条边,然后走到其另一端点。

第二个人想要把棋子走到终点,而第一个人想要阻止他。两个人都按最佳策略操作。
问,棋子是否能够到达终点?

1 ≤ T ≤ 20 ,   n ≤ 100 ,   1 ≤ m ≤ 10000 ,   1 ≤ s t , e n ≤ n ,   s t ! = e n 1≤T≤20,\ n≤100,\ 1≤m≤10000,\ 1≤st,en≤n,\ st != en 1T20, n100, 1m10000, 1st,enn, st!=en

思路
第二个人想要阻止棋子走到终点,其肯定会把走到终点路径上的边拿掉。而如果一个点走到终点的路径有不少于两条,那么第二人不论拿掉哪条边都不能阻止棋子到达终点。
也就是说,如果有一个点相连了至少两个必胜点的话,那么这个点也是必胜点,如果能够走到这个点,那么第一个人就必胜。

如果正着从起点考虑,并不知道是否该点有两条路径能够到达终点,不能确定当前点相连的是否是必胜点。

反过来,从终点往前推,从终点出发,终点肯定是必胜点。如果一个点能由两个至少两个必胜点到达的话,由双向边,那么这个点往后走就能至少到达两个必胜点,无论切掉哪个边都能到达必胜点,胜利不可阻挡,当前点就是必胜点。

把所有必胜点放到队列里去更新相邻点,如果一个点被走到两次,说明其就是必胜点,放到队列。最后看起点是不是必胜点即可。

Code

#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];
int st, en;
vector<int> e[N];
int f[N], vis[N];

void bfs()
{
	queue<int> que;
	que.push(en);
	f[en] = 1;
	
	while(que.size())
	{
		int x = que.front(); que.pop();
		
		for(int tx : e[x])
		{
			if(f[tx]) continue;
			vis[tx] ++;
			if(vis[tx] == 2)
			{
				f[tx] = 1;
				que.push(tx);
			}
		}
	}
}

signed main(){
	Ios;
	cin >> T;
	while(T--)
	{
		cin >> n >> m >> st >> en;
		
		for(int i=1;i<=n;i++){
			e[i].clear();
			f[i] = 0;
			vis[i] = 0;
		}
		
		while(m--)
		{
			int x, y; cin >> x >> y;
			e[x].push_back(y);
			e[y].push_back(x);
		}
		
		bfs();
		
		if(f[st]) cout << "Join Player\n";
		else cout << "Cut Player\n";
	}
	
	return 0;
}

经验
正着复杂的话就想着能不能反着来,正难则反!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值