HDU 3715 Go Deeper(2-SAT)
http://acm.hdu.edu.cn/showproblem.php?pid=3715
题意:
有下面一个递归程序:
go(int dep, int n, int m)
begin
output the value of dep.
if dep < m and x[a[dep]] + x[b[dep]]!= c[dep] then go(dep + 1, n, m)
end
问你dep最多能达到什么值?由题意可知dep<=m,其中a与b与c数组都是m大小的,数组下标从0到m,然后x数组是下标从0到n-1的.且c数组的取值为0或1或2.a与b数组的取值是0到n-1.x数组的取值是0或1.
现在给出了a,b,c数组对应下标的所有值,但是还不知道x数组的取值,问你如果你来设定x的值,可以使dep最大达到多少?
分析:
由于x数组只能去0或1,可以看出该题就是2-SAT问题. 我们只要2分dep的值即可的出解.
假设当前dep=mid,然后对于下标从0到mid-1来说,有下面关系:
x[a[0]]+x[b[0]] != c[0]
x[a[1]]+x[b[1]] != c[1]
…
x[a[mid-1]]+x[b[mid-1]] !=c[mid-1]
然后我们合理的设置一下x数组的值,看看是否能满足这前mid个条件.由于a,b,c数组的值都已经知道了,比如 a[0]=1,b[0]=2,c[0]=2
有x[1]+x[2] !=2 ,那么我们可以推出边: add_clause(1,1,2,0)且 add_clause(2,1,1,0).
一般性结论有:
x[a]+x[b]= 0 -> add(a,0,b,1) add(b,0,a,1)
x[a]+x[b]=1 ->add(a,1,b,1) add(a,0,b,0) add(b,1,a,1)add(b,0,a,0)
x[a]+x[b]=2 ->add(a,1,b,0) add(b,1,a,0)
二分dep,建图2-SAT,直接解决.
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int maxn= 200 +10; const int maxm= 10000+10; int n,m; int a[maxm],b[maxm],c[maxm]; struct TwoSAT { int n; vector<int> G[maxn*2]; int S[maxn*2],c; bool mark[maxn*2]; bool dfs(int x) { if(mark[x^1]) return false; if(mark[x]) return true; mark[x]=true; S[c++]=x; for(int i=0;i<G[x].size();i++) if(!dfs(G[x][i])) return false; return true; } void init(int n) { this->n = n; for(int i=0;i<2*n;i++) G[i].clear(); memset(mark,0,sizeof(mark)); } void add_clause(int x,int xval,int y,int yval) { x=x*2+xval; y=y*2+yval; G[x].push_back(y); } bool solve() { for(int i=0;i<2*n;i+=2) if(!mark[i] && !mark[i+1]) { c=0; if(!dfs(i)) { while(c>0) mark[S[--c]]=false; if(!dfs(i+1)) return false; } } return true; } }TS; bool ok(int mid) { TS.init(n); for(int i=0;i<mid;i++) { if(c[i]==0) { TS.add_clause(a[i],0,b[i],1); TS.add_clause(b[i],0,a[i],1); } else if(c[i]==1) { TS.add_clause(a[i],0,b[i],0); TS.add_clause(a[i],1,b[i],1); TS.add_clause(b[i],0,a[i],0); TS.add_clause(b[i],1,a[i],1); } else if(c[i]==2) { TS.add_clause(a[i],1,b[i],0); TS.add_clause(b[i],1,a[i],0); } } return TS.solve(); } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]); int L=0,R=m; while(R>L) { int mid = L+(R-L+1)/2; if(ok(mid)) L=mid; else R=mid-1; } printf("%d\n",L); } return 0; }