P6560 [SBCOI2020] 时光的流逝(博弈论,拓扑排序)

这篇博客探讨了如何利用博弈论解决图论问题,通过两个代码示例展示了从必败点开始的反向拓扑排序策略,确定图中节点的必胜或必败状态。代码1和代码2都采用了不同的方法,但核心思想是找出能到达必败点的节点作为必胜点。博客还强调了在分析过程中理解关键条件的重要性。
摘要由CSDN通过智能技术生成

题目链接

思路

  • 显然这是道博弈论的题,而且是图上的。
  • 我们可以在图上标记必胜点和必败点,显然终点是必败点,所有没有出边的点是必败点。
  • 我们可以发现规律:能到必败点的一定是必胜点;只能到必胜点的是必败点
  • 起点状态未知,终点状态已知,所以我们可以从已知的必败点开始按照拓扑序反向bfs,判断每个点的状态。

代码1(我有点没搞明白)

#include<cstdio>
#include<queue>
#include<cstring>
#define ri register int
#define max(a,b) a>b?a:b
using namespace std;
const int maxn=1e5+21;
struct E{
	int v,n;
}e[5*maxn];
int head[maxn],cnt;
int o[maxn],in[maxn],out[maxn],f[maxn];
int n,m,Q,s,t;
int read(){//快读 
	int x=0;char c=getchar();
	while(c>'9'||c<'0') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
void add(int u,int v){//建边 
	e[++cnt].v=v;
	e[cnt].n=head[u];
	head[u]=cnt;
}
int a[maxn];
queue<int>q;
void d(int u){//在图中删去u这个节点 
	f[u]=1;//u已经删去了 
	for(ri i=head[u];i;i=e[i].n){
		int v=e[i].v;
		out[v]--;
		if(!out[v]) q.push(v);//出度为零则入队 
	}
}
void bfs(){
	for(ri i=1;i<=n;++i) if(!out[i]) a[i]=-1,q.push(i);//出度为零的入队 
	a[t]=-1,q.push(t);//终点入队 
	while(q.size()){
		int u=q.front();q.pop();
		if(f[u]) continue;//如果该点已删除 
		if(a[s]!=0) break;//起点状态已知就不必再搜索了 
		d(u);
		if(a[u]==-1){//能到必败点的是必胜点 
			for(ri i=head[u];i;i=e[i].n)
				if(!a[e[i].v]) //重点!!! 为什么? 
					a[e[i].v]=1,d(e[i].v);//删去e[i].v这个点 
		}
		else if(!in[u]) a[u]=-1;//重点!!!  若无法到其他点,显然必败 
		else{
			a[u]=-1;//重点!!! 
			for(ri i=head[u];i;i=e[i].n)
				if(!a[e[i].v]) a[e[i].v]=1,d(e[i].v);
		}
	}
	while(q.size()) q.pop();//记得清空队列 
}
int main(){
	n=read(),m=read(),Q=read();
	for(ri i=1;i<=m;++i){
		int u=read(),v=read();
		add(v,u);o[u]++,in[v]++;
	}
	while(Q--){
		s=read(),t=read();
		for(ri i=1;i<=n;++i) out[i]=o[i];//出度每次复制 
		memset(a,0,sizeof a);
		memset(f,0,sizeof f);
		bfs();
		printf("%d\n",a[s]);
	}
	return 0;
}

代码2(个人认为更易接受)

#include<cstdio>
#include<queue>
#define ri register int
#define max(a,b) a>b?a:b
using namespace std;
const int maxn=1e5+21;
struct E{
	int v,n;
}e[5*maxn];
int head[maxn],cnt;
int o[maxn],out[maxn];
int n,m,q,s,t;
int read(){//快读 
	int x=0;char c=getchar();
	while(c>'9'||c<'0') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
void add(int u,int v){//建边 
	e[++cnt].v=v;
	e[cnt].n=head[u];
	head[u]=cnt;
}
int a[maxn];//-1必败,0不确定,1必胜 
void bfs(){
	queue<int>q;
	for(ri i=1;i<=n;++i) if(!out[i]||i==t) a[i]=-1,q.push(i);//终点和必败点入队 
	while(q.size())
	{
		int u=q.front();q.pop();
		for(ri i=head[u];i;i=e[i].n)
		{
			int v=e[i].v;
			if(a[v]!=0) continue;//v更新过则不用再更新 
			if(a[u]==-1) a[v]=1,q.push(v);//能到必败点的一定是必胜点 
			if(a[u]==1)//能到必胜点的不一定是必败点 
			{
				out[v]--;
				if(!out[v]) q.push(v),a[v]=-1;//如果出度为零,则是必败点
				//因为当v的出度为零时,说明它不能到必败点,那么它就是必败点 
			}
		}
	}
}
int main(){
	n=read(),m=read(),q=read();
	for(ri i=1;i<=m;++i)
	{
		int u=read(),v=read();
		add(v,u);o[u]++;
	}
	while(q--)
	{
		s=read(),t=read();
		for(ri i=1;i<=n;++i) out[i]=o[i],a[i]=0;//复制,清零 
		bfs();
		printf("%d\n",a[s]);
	}
	return 0;
}

我的博弈论题单

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Robin_w2321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值