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 ;
}