2-sat神题。。
告诉是2-sat我也完全想不到正解。
看了看题解其实一步步分析也不算很难
这个题首先是要围绕每个人是否是犯人和每句话是否是真话来思考
首先要明确的是:
1.好人不说谎话
2.说了谎话的只能是坏人
所以我们就知道了一组对称的限制条件:
好人->之前没说过谎话
之前说过谎话->坏人
如何判断一句话是否是谎话?
这个人说的话和事实不相符,这也是一种限制条件
这时我们发现还有一个很重要的条件:哪怕是坏人,也最多只说一句谎话
所以如果当前是谎话,那这个人之前所说的所有话和之后所说的所有话都是真话
所以我们在每个点应该存的是在这个点之前(包括该点)有没有说过谎话
利用上面这些限制,就可以做出这道题啦~
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define M 1000010 5 using namespace std; 6 struct point{ 7 int to,next; 8 }e[M<<1]; 9 int n,m,num,cnt,tim,tot,top; 10 int head[M],dfn[M],low[M],st[M],ans[M],co[M],pre[M]; 11 bool vis[M]; 12 int is_crm(int a,int b) {return a+b*n;} 13 //0->not 14 //1->yes 15 int word(int a,int b) {return (n<<1)+a+b*m;} 16 //0->true 17 //1->false 18 void add(int from,int to) 19 { 20 e[++num].next=head[from]; 21 e[num].to=to; 22 head[from]=num; 23 } 24 void tarjan(int x) 25 { 26 dfn[x]=low[x]=++tim; 27 st[++top]=x; 28 vis[x]=true; 29 for(int i=head[x];i;i=e[i].next) 30 { 31 int to=e[i].to; 32 if(!dfn[to]) 33 { 34 tarjan(to); 35 low[x]=min(low[x],low[to]); 36 } 37 else if(vis[to]) low[x]=min(low[x],low[to]); 38 } 39 if(low[x]==dfn[x]) 40 { 41 tot++; 42 while(st[top+1]!=x) 43 { 44 co[st[top]]=tot; 45 vis[st[top]]=false; 46 top--; 47 } 48 } 49 } 50 int main() 51 { 52 scanf("%d%d",&n,&m); 53 for(int i=1;i<=n;i++) pre[i]=2*m+1; 54 for(int i=1;i<=m;i++) 55 { 56 int x,y,opt; scanf("%d%d%d",&x,&y,&opt); 57 opt^=1; 58 add(is_crm(y,opt^1),word(pre[x],0)); 59 //如果事实和他说的不是一回事,那他说的就是假话 60 //如果这句话是假话,在此之前的所有话皆为真话 61 add(word(pre[x],1),is_crm(y,opt)); 62 //如果这个人之前说过假话了,那么这句话就是真话 63 //他说这个人是什么就是什么 64 add(word(i,0),is_crm(y,opt)); 65 //如果这句话和之前都是真话,他说这个人是什么就是什么 66 add(is_crm(y,opt^1),word(i,1)); 67 //这句话说的是反话,那这句话及之前的所有话中有反话 68 add(word(i,0),word(pre[x],0)); 69 //如果这句话及之前的所有话皆为真,那么这句话之前所有话皆为真 70 add(word(pre[x],1),word(i,1)); 71 //如果之前有假的,这句话及之前的话也有假的 72 pre[x]=i; 73 } 74 //判断是否是罪犯 75 for(int i=1;i<=n;i++) 76 { 77 add(word(pre[i],1),is_crm(i,1)); 78 add(is_crm(i,0),word(pre[i],0)); 79 } 80 for(int i=1;i<=(n+m)<<1;i++) 81 if(!dfn[i]) 82 tarjan(i); 83 for(int i=1;i<=n;i++) 84 { 85 if(co[i]==co[i+n]) 86 { 87 printf("Impossible"); 88 return 0; 89 } 90 else if(co[i]>co[i+n]) ans[++cnt]=i; 91 } 92 printf("%d\n",cnt); 93 for(int i=1;i<=cnt;i++) printf("%d ",ans[i]); 94 return 0; 95 }