2-SAT建模+可行性判断好题。
题目中说每个评委的要求至少要满足一个,而汉式和满式只能选择做一种,
那么这个就是模型:
(A,B)不能同时不取
此时的连边方法是:A'与B连,B'与A连
然后套上可行性判断的模板来做就行了。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <stack> #define M 2005 using namespace std; char s[10]; stack<int> S; int tot,h[M],sccno[M],pre[M],lowlink[M],n,m,T,dfs_clock,scc_cnt; struct edge { int ne,y; }e[M*M]; void dfs(int u) { pre[u]=lowlink[u]=++dfs_clock; S.push(u); for (int i=h[u];i;i=e[i].ne) { int v=e[i].y; if (!pre[v]) { dfs(v); lowlink[u]=min(lowlink[v],lowlink[u]); } else if (!sccno[v]) lowlink[u]=min(lowlink[u],pre[v]); } if (lowlink[u]==pre[u]) { scc_cnt++; for (;;) { int x=S.top(); S.pop(); sccno[x]=scc_cnt; if (x==u) break; } } } void Findscc() { dfs_clock=scc_cnt=0; memset(pre,0,sizeof(pre)); memset(sccno,0,sizeof(sccno)); for (int i=2;i<=2*n+1;i++) if (!pre[i]) dfs(i); } void Add(int x,int y) { tot++; e[tot].ne=h[x]; e[tot].y=y; h[x]=tot; } int main() { scanf("%d",&T); while (T--) { tot=0; scanf("%d%d",&n,&m); for (int i=2;i<=n*2+1;i++) h[i]=0; for (int i=1;i<=m;i++) { int x,y; char c1,c2; getchar(); scanf("%c%d %c%d",&c1,&x,&c2,&y); x*=2,y*=2; x+=(c1=='m'); y+=(c2=='m'); Add(x^1,y);Add(y^1,x); } Findscc(); bool f=true; for (int i=2;i<=2*n;i+=2) if (sccno[i]==sccno[i^1]) { f=false; break; } if (f) printf("GOOD\n"); else printf("BAD\n"); } return 0; }
感悟:
1.WA:把读入按照char[]来处理,默认是一位数字了。。
2.对2-SAT的理解:
Q:为什么要把图反向呢?
A:当选择了A点,那么A'被标记为不选择,此时要把不选择的标记向前面传递(A'不选择,A'之前的一定不选择,A'之后的不一定)。
如果此时图没有反向,那么要找到A'之前的点很麻烦(现在连得边都是向后走的);
而反向之后直接沿着边走,就相当于沿着反向边走了!
3.做了三道2—SAT题目,2—SAT题目建模方法为:
首先找到每个情况中(A,B)是哪一种模型:
那么A所对应的A'是谁呢?
有的题目中需要枚举每一种情况,与A不相容的都是A'(【POJ 3683】);
而在本题中,与A不相容的都已知了(h与m不相容),直接建图即可。