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;
}