转载请注明出处:http://blog.csdn.net/u013912596/article/details/40985211
题目大意:
Alice和Bob玩若干轮石头剪刀布的游戏,Alice已经知道了Bob在每一轮会出什么,但是Bob会给出一些Alice的限制条件,问Alice在不打破这些限制的情况下,有没有可能赢。
限制格式为(i,j,w),i,j代表第几轮,w=1的话,要求Alice在第i轮和第j轮的策略不能相同,w=0的话,要求Alice在第i轮和第j轮的策略必须相同。
思路:
a[i]=2时,Alice只能选择2,3,令此时的2为true,3为false。
a[i]=3时,Alice只能选择3,1,令此时的1为true,3为false。
可能有的同学会说,这不是有的矛盾了吗。。。(博主是蒟蒻。。当时也在纠结这个问题。。),后来发现其实没有影响,只是表示这两种状态而已,只要一开始定好规则,按照这个规则建边就好了。
好!下面开始认真的建边了!
对每条限制条件进行处理(Ai,Aj表示Alice在该轮的状态,true or false):
1.当a[i]==a[j],(1).w==1(不同):Ai xor Aj =1
(2).w==0(相同):Ai xor Aj =0
2.当a[i]==1&&a[j]==2时,(1).w==1(不同):Ai or ~Aj =1
(2).w==0(相同):~Ai and Aj =1(这个地方做得时候和上一条建边高反了T.T,结果调了一天。。。泪面。。)
3.当a[i]==2&&a[j]==3时,(1).w==1(不同):~Ai or ~Aj =1
(2).w==0(相同):Ai and Aj =1
4.当a[i]==1&&a[j]==3时,(1).w==1(不同):Ai or Aj =1
(2).w==0(相同):~Ai and ~Aj =1
好!有上述条件之后,我们就可以开始建边啦~(如果a[i]==2,a[j]==1什么的,交换一下 i , j 就好啦)下面是各个公式对应的建边方式:
把每个值是1(a)和0(~a)为两种状态,分清楚各种操作的本质就很简单了
AND 结果为1:建边 ~x->x,~y->y (两个数必须全为1)
AND 结果为0:建边 y->~x,x->~y (两个数至少有一个为0)
OR 结果为1:建边 ~x->y,~y->x (两个数至少有一个为1)
OR 结果为0:建边 x->~x,y->~y (两个数必须全为0)
XOR 结果为1:建边 x->~y,y->~x,~y->x,~x->y (两个数必须不同)
XOR 结果为0:建边 x->y,y->x,~x->~y,~y->~x (两个数必须相同)
好吧。。这道题建完边,用模板跑一边,结果就出来了,挺简单的。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int V;//mind assign
const int MAX_V=22222;
vector<int> G[MAX_V];
vector<int> rG[MAX_V];
vector<int> vs;
bool used[MAX_V];
int cmp[MAX_V];
int a[MAX_V];
void add_edge(int from,int to){
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v){
used[v]=true;
for(int i=0;i<G[v].size();i++){
if(!used[G[v][i]]) dfs(G[v][i]);
}
vs.push_back(v);
}
void rdfs(int v,int k){
used[v]=true;
cmp[v]=k;
for(int i=0;i<rG[v].size();i++)
if(!used[rG[v][i]]) rdfs(rG[v][i],k);
}
int scc(){
memset(used,0,sizeof(used));
vs.clear();
for(int v=0;v<V;v++){
if(!used[v]) dfs(v);
}
memset(used,0,sizeof(used));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!used[vs[i]]) rdfs(vs[i],k++);
}
return k;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("D:/in.txt","r",stdin);
//freopen("D:/out.txt","w",stdout);
#endif // ONLINE_JUDGE
int T;scanf("%d",&T);
int cas=1;
while(T--){
for(int i=0;i<MAX_V;i++) G[i].clear();
for(int i=0;i<MAX_V;i++) rG[i].clear();
memset(cmp,0,sizeof(cmp));
int n,m;
printf("Case #%d: ",cas++);
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",a+i);
}
V=2*n;
for(int k=1;k<=m;k++){
int i,j,w;
scanf("%d%d%d",&i,&j,&w);
i--;j--;
if(a[i]==a[j]){
if(w==1){
add_edge(i,j+n);
add_edge(j,i+n);
add_edge(i+n,j);
add_edge(j+n,i);
}else{
add_edge(i,j);
add_edge(j,i);
add_edge(i+n,j+n);
add_edge(j+n,i+n);
}
}else{
if(a[i]+a[j]==3){
if(a[i]!=1) swap(i,j);//这里必须要交换保证顺序正确
if(w==1){
add_edge(i+n,j+n);
add_edge(j,i);
}else{
add_edge(i,i+n);
add_edge(j+n,j);
}
}else if(a[i]+a[j]==4){
//if(a[i]!=1) swap(i,j);//这里可以不用交换,因为操作一样
if(w==1){
add_edge(i,j+n);
add_edge(j,i+n);
}else{
add_edge(i+n,i);
add_edge(j+n,j);
}
}else{
//if(a[i]!=2) swap(i,j);//同上
if(w==1){
add_edge(i+n,j);
add_edge(j+n,i);
}else{
add_edge(i,i+n);
add_edge(j,j+n);
}
}
}
}
//after build edges
scc();
bool isCon=false;
for(int i=0;i<n;i++){
if(cmp[i]==cmp[n+i]){
printf("no\n");
isCon=true;
break;
}
}
if(isCon) continue;
printf("yes\n");
}
return 0;
}