2 3 3 1 1 1 1 2 1 1 3 1 2 3 1 5 5 1 2 3 2 1 1 2 1 1 3 1 1 4 1 1 5 1 2 3 0
Case #1: no Case #2: yesHint'Rock, Paper and Scissors' is a game which played by two person. They should play Rock, Paper or Scissors by their hands at the same time. Rock defeats scissors, scissors defeats paper and paper defeats rock. If two people play the same item, the game is tied..
题目大意:ACM两位神牛Alice和Bob又来玩游戏了,这次他们玩石头剪刀布。规定石头、布、剪刀分别用1、2、3表示。他们准备玩n局,现在Alice已知Bob的n局的出法,但是为了公平,对于Alice,Bob给了m个限制,每个限制3个参数:a b k。k=0或1。当k = 0的时候,表示第a局和第j局Alice必须出相同的手势,k= 1表示Alice在第a局和第j局必须出不同的手势。如果n轮比完,Alice一局没输的话,Alice赢,否则Bob赢。求Alice是否有赢的可能。
题目分析:因为每一轮有3种手势,乍一看是3-sat问题。但因为对于Alice来说,一局也不能输,所以只要n局保持不败就可以了,所以对于Alice来说,每一局其实只有2种情况,于是利用这个条件可以将此3-sat问题转化为2-sat问题。
先来看一张表:
Bob Alice
平 赢
1 1 2
2 2 3
3 3 1
一共就这6种情况。可以看出,无论Bob第a局和第b局出什么,Alice要保持不败,至少有一种手势可以在第a局和第b局同时做。所以我们先根据Bob第i局的手势,找出Alice第i局保持不败的2种手势,然后开始建图。
如果第a局和第b局Alice的手势要不同,那么矛盾的情况是Alice第a局和第b局手势相同,那么枚举这4种情况,找到矛盾的点对,建边,如果ab矛盾,建a->b',b->a'。
如果第a局和第b局Alice的手势要相同,那么矛盾的情况是Alice第a局和第b局手势不同,根据分析可知,无论第a局和第b局Bob出什么手势,Alice在这两局保持不败的策略中至少有一对是相同的,先考虑2对都相同的,即Bob在第a局和第b局手势相同,那么直接建边a->b,b->a,a + n->b + n,b + n->a + n(假设ab同,a+n b+n同),一共4条边。如果没有2对相同,那么至少一对相同,依次枚举,找到那对相同的,只有这对相同的合理,其他的都不合理,那么仿照经典的And和Or操作中的建图,假设ab相同,那么只有ab是合法的,所以直接建a+n->a,b+n->b。
详情请见代码:
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 10001; const int M = 10001; struct edge { int to,next; }g[M<<1]; int head[N<<1]; int scc[N<<1]; int vis[N<<1]; int stack1[N<<1]; int stack2[N<<1]; int n,m,num; int lcm[N][2]; bool flag; void init() { memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); memset(scc,0,sizeof(scc)); stack1[0] = stack2[0] = num = 0; flag = true; } void build(int s,int e) { g[num].to = e; g[num].next = head[s]; head[s] = num ++; } void dfs(int cur,int &sig,int &cnt) { if(!flag) return; vis[cur] = ++sig; stack1[++stack1[0]] = cur; stack2[++stack2[0]] = cur; for(int i = head[cur];~i;i = g[i].next) { if(!vis[g[i].to]) dfs(g[i].to,sig,cnt); else { if(!scc[g[i].to]) { while(vis[stack2[stack2[0]]] > vis[g[i].to]) stack2[0] --; } } } if(stack2[stack2[0]] == cur) { stack2[0] --; cnt ++; do { scc[stack1[stack1[0]]] = cnt; int tmp = stack1[stack1[0]]; if((tmp > n && scc[tmp - n] == cnt) || (tmp <= n && scc[tmp + n] == cnt)) { flag = false; return; } }while(stack1[stack1[0] --] != cur); } } void Gabow() { int i,sig,cnt; sig = cnt = 0; for(i = 1;i <= n + n && flag;i ++) if(!vis[i]) dfs(i,sig,cnt); } int nextint() { char c; int ret; while((c = getchar()) > '9' || c < '0') ; ret = c - '0'; while((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + c - '0'; return ret; } int main() { int i,j,T,t,a,b; scanf("%d",&T); int cas = 0; while(T --) { scanf("%d%d",&n,&m); for(i = 1;i <= n;i ++) { scanf("%d",&t); lcm[i][0] = t;//tie lcm[i][1] = t + 1;//win if(lcm[i][1] > 3) lcm[i][1] = 1; if(lcm[i][0] > lcm[i][1]) lcm[i][0] ^= lcm[i][1] ^= lcm[i][0] ^= lcm[i][1]; } init(); while(m --) { scanf("%d%d%d",&a,&b,&t); if(t)//different {//建图小心 找矛盾 if(lcm[a][0] == lcm[b][0]) { build(a,b + n); build(b,a + n); } if(lcm[a][0] == lcm[b][1]) { build(a,b); build(b + n,a + n); } if(lcm[a][1] == lcm[b][0]) { build(a + n,b + n); build(b,a); } if(lcm[a][1] == lcm[b][1]) { build(a + n,b); build(b + n,a); } } else//same { if(lcm[a][0] == lcm[b][0] && lcm[a][1] == lcm[b][1]) { build(a,b); build(b,a); build(a + n,b + n); build(b + n,a + n); } else {//必有一对相同的,只能选这一对 if(lcm[a][0] == lcm[b][0]) { build(a + n,a); build(b + n,b); } if(lcm[a][0] == lcm[b][1]) { build(a + n,a); build(b,b + n); } if(lcm[a][1] == lcm[b][0]) { build(a,a + n); build(b + n,b); } if(lcm[a][1] == lcm[b][1]) { build(a,a + n); build(b,b + n); } } } } Gabow(); printf("Case #%d: ",++cas); if(flag) printf("yes\n"); else printf("no\n"); } return 0; } //0MS 556K