题目大意
给定一个无向图,每一轮,两人从x,y两点有先后地选择不移动或移动到相邻点(两人都进行选择才算一轮)。当最后一轮结束时,若两人都达到z点或都到不了z点,则平局输出2。若先手的人到了,后手未到输出1,反之输出3。两人除了起点和终点外,不能处在相同节点上。
题解
通过BFS找到z到x,y的最短路径分别为dis[x],dis[y]。
- 若dis[x]!=dis[y]则谁路劲短谁获胜。
- 若dis[x]==dis[y],则先手必不败,可能平局。
简单证明:
一 两人最短路除z点外无交点
- dis[x]!=dis[y]
i.若先手的最短路小于后手,易得先手必胜。
ii.若后手的最短路小于先手,考虑极限情况,dis[x]=dis[y]+1。每轮两人都贪心地向z点移动(因为最短路无交点),dis[x]–,dis[y]–。可以看到,每一轮结束时,dis[x]一定大于dis[y],所以dis[y]能先于归零,即后手能先到达终点,后手必胜。 - dis[x]==dis[y]
两者最短路无交点,所以当最后一轮结束时,两者同时到达z点,平局。
二 两人最短路除z点有交点
- dis[x]!=dis[y]
假设dis[x]<dis[y],且两者的交点为k。若忽略交点k的影响,可以得出先手必胜的结论。能想到的后手若想取胜的方法就是:在先手还未经过k点前,在任意一轮结束时先于先手到达k点后停留在k点,当先手到达k的前一点时就会由于后手已经在k点,会选择停留直到后手走开,由此后手转化为先手就能取胜。**但是后手必定不能先于先手到达交点k。**因为交点k到z点的最短路的值dis[k]是唯一确定的,dis[x]=dis[k]+x到k的最短路程。dis[y]=dis[y]+y到k的最短路程。若后手能先于先手到达k点,就会得到dis[x]>dis[y]的结论。所以不存在这种情况,当最短路不同时任然是谁的最短短谁取胜。 - dis[x]==dis[y]
同理,忽略交点k的影响,就可以得到平局的结论。现在考虑k点能怎样影响胜负。考虑极限情况,只有唯一交点k,先手和后手都得从该路径到达z点,因为先手能先到达k点,后手也得通过k点,就必须等待先手走开,则先手胜,否则平局。推到一般情况,存在多个交点,可以得到,若后手的所有最短路径中都有k点,并且先手存在一条最短路劲也有k点则先手胜,否则平局。(题目里的样例第一个就该情况)
标程 AC代码
虽然最短路的值是唯一确定的,但是可能存在多条这样的路径。不方便通过交点去查看,就按标程里的DP来。
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int N=2e3+5;
const int inf=1e9;
struct node{
int a,b,c;
};
int dp[N][N][2];
int n,m,x,y,z;
int dis[N];
vector<int> pre[N];
vector<int> e[N];
vector<node> v;
int f(int x,int y,int z,int k)
{
if((k==0)&&(x==z||y==z))//k==0限制判断都得是每轮结束
{
if(x==z&&y==z)
return 2;
return 1;//不是平局就是先手胜
}
if(dp[x][y][k])
return dp[x][y][k];
int kk;
/*
两人最短大小相同,先手必不败
如果k==0则现在由先手走,先手就会选择使自己获胜的方案,若不存在,则平局
如果k==1则现在由后手走,后手只能尽量选择平局的方案,若不存在,则先手胜
*/
if(k==0)
{
kk=2;
for(int i=0;i<pre[x].size();i++)
{
int u=pre[x][i];
if(u!=y||u==z)
{
int tt=f(u,y,z,k^1);
if(tt==1)//若存在一种方案使先手获胜,则选择该种方案。
kk=1;
}
}
dp[x][y][k]=kk;
}
else
{
kk=1;
for(int i=0;i<pre[y].size();i++)
{
int u=pre[y][i];
if(u!=x||u==z)
{
int tt=f(x,u,z,k^1);
if(tt==2)//若存在一种方案使平局,则选择该种方案。
kk=2;
}
}
dp[x][y][k]=kk;
}
v.push_back({x,y,k});
return dp[x][y][k];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
for(int i=1;i<=n;i++)
e[i].clear();
scanf("%d%d",&n,&m);
scanf("%d%d%d",&x,&y,&z);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++)
dis[i]=inf;
dis[z]=0;
queue<int> que;
que.push(z);
while(!que.empty())//BFS求最短路
{
int x=que.front();
que.pop();
for(int i=0;i<e[x].size();i++)
{
int v=e[x][i];
if(dis[v]==inf)
{
dis[v]=dis[x]+1;
que.push(v);
}
}
}
if(dis[x]!=dis[y])
{
printf("%d\n",dis[x]>dis[y]?3:1);
continue;
}
if(dis[x]==inf)
{
printf("2\n");
continue;
}
for(int i=1;i<=n;i++)//找出所有的最短路径
{
for(int j=0;j<e[i].size();j++)
if(dis[i]==dis[e[i][j]]+1)
pre[i].push_back(e[i][j]);
}
printf("%d\n",f(x,y,z,0));
for(int i=1;i<=n;i++)
pre[i].clear();
for(int i=0;i<v.size();i++)
dp[v[i].a][v[i].b][v[i].c]=0;//不能使用memset会T
v.clear();
}
return 0;
}