http://acm.hdu.edu.cn/showproblem.php?pid=4115
去年成都现场赛的一道题,那时候还不知道什么叫2-Sat ,今天终于1Y。
题意:
有两个人玩一个石头剪刀布的游戏,两个人连续玩N轮,给出其中
一个人的N轮出的情况和该人对另外一个人的一些限制条件,有两种限制:
每种限制表示为:(a,b,c) ,如果c==0 则表示该人对另外一个人的限制为第a
局和第b局出的应该一样,如果c==1表示不一样,问另外一个人是否有赢的
可能。
思路:
从表面上看,对于每一局,另外一个人有3中选择,似乎不能用2-Sat,但是
仔细想一下就会发现,其实3中选择中,只有两种是合法的,也就是说如果
选择另外一种的话,那个人就会输了,所以只要考虑两种不输的情况就可以了。
这样就可以按照那个人的序列确定出另外一个人的不输序列,并且没一轮只有
两种选择,这样就转化为标准的2-Sat问题了,然后就是建图,(a,b,c):
当c == 0 时, 要求a和b相同,枚举a的合法出牌,若果对于a的某种出牌,b对
应的是不合法出牌,也就说明a不能选择这种出法,这可以通过
加边(a , a')来实现。
当 c == 1时:要求a和b不相同,枚举a的合法出牌,如果对应的是b的不合法出
牌,这也就说明,如果a出这张牌,b可以随便出两张牌中的任意
一张,这种情况不能加边;如果对应的是合法边,那么这时候b
只有一种出牌情况,只要找出加边即可。
建完图之后就用tarjin缩点判断是否存在解即可。
代码:
#include<stdio.h> #include<string.h> int T,N,M; int Bob[10010] ; int Gr[10010*2], Gnext[10*10010] , Gv[10*10010] , Gc ; int res[10010*2] ; bool ok[10010][5] ; int nn[10010][5] ; const int MAXN = 20010 ; int dfn[MAXN] ,low[MAXN] ,stack[MAXN] ,belong[MAXN] ; bool in[MAXN] ; int top ,idx , Bcnt ; void tarjin(int u){ int v ; low[u] = dfn[u] = ++idx ; stack[++top] = u ; in[u] = 1 ; for(int i=Gr[u];i!=-1;i=Gnext[i]){ v = Gv[i] ; if( !dfn[v] ){ tarjin(v) ; if( low[v] < low[u]) low[u] = low[v] ; } else if( in[v] && dfn[v] < low[u]) low[u] = dfn[v] ; } if( low[u] == dfn[u] ){ Bcnt++ ; do{ v = stack[top--] ; in[v] = 0 ; belong[v] = Bcnt ; }while(u != v) ; } } bool solve(){ top = idx = Bcnt = 0 ; memset(dfn , 0 ,sizeof(dfn)); memset(in , 0 ,sizeof(in)); for(int i=1;i<=2*N;i++){ if( !dfn[i] ) tarjin(i) ; } for(int i=1;i<=N;i++){ if( belong[i] == belong[i+N] ) return false ; } return true ; } void add(int a, int b){ Gv[Gc] = b ; Gnext[Gc] = Gr[a] ; Gr[a] = Gc++ ; } int main(){ scanf("%d",&T); int cas = 0 ,a,b,c; int aa ,bb; while(T--){ ++cas ; scanf("%d%d",&N,&M); memset(ok , 0, sizeof(ok)); for(int i=1;i<=N;i++){ scanf("%d",&Bob[i]); Bob[i]-- ; res[i] = Bob[i]; res[i+N] = (Bob[i] + 1) % 3 ; ok[i][ res[i] ] = ok[i][ res[i+N] ] = 1 ; nn[i][ res[i] ] = i ; nn[i][ res[i+N] ] = i + N ; } memset(Gr, -1,sizeof(Gr)); Gc = 0 ; for(int i=1;i<=M;i++){ scanf("%d%d%d",&a,&b,&c); if(c == 0){ aa = res[a] ; bb = res[a+N] ; if( ok[b][aa]==0 ) add(a,a+N); else add(a,nn[b][aa]) ; if( ok[b][bb] == 0) add(a+N,a) ; else add(a+N,nn[b][bb]); aa = res[b] ; bb = res[b+N] ; if( ok[a][aa] == 0) add(b,b+N); else add(b,nn[a][aa]) ; if( ok[a][bb] == 0) add(b+N,b) ; else add(b+N,nn[a][bb]) ; } else{ aa = res[a] ; bb = res[a+N] ; if( ok[b][aa] == 1 ){ for(int j=0;j<3;j++){ if(aa==j || ok[b][j]==0) continue ; add(a, nn[b][j] ); } } if( ok[b][bb] == 1){ for(int j=0;j<3;j++){ if(bb==j || ok[b][j]==0) continue ; add(a+N,nn[b][j]); } } aa = res[b] ; bb = res[b+N] ; if( ok[a][aa] == 1 ){ for(int j=0;j<3;j++){ if(aa==j || ok[a][j]==0) continue ; add(b, nn[a][j] ); } } if( ok[a][bb] == 1){ for(int j=0;j<3;j++){ if(bb==j || ok[a][j]==0) continue ; add(b+N,nn[a][j]); } } } } printf("Case #%d: ",cas); if( solve() ) printf("yes\n"); else printf("no\n"); } return 0 ; }