Go Deeper
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2860 Accepted Submission(s): 916
Problem Description
Here is a procedure's pseudocode:
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
In this code n is an integer. a, b, c and x are 4 arrays of integers. The index of array always starts from 0. Array a and b consist of non-negative integers smaller than n. Array x consists of only 0 and 1. Array c consists of only 0, 1 and 2. The lengths of array a, b and c are m while the length of array x is n. Given the elements of array a, b, and c, when we call the procedure go(0, n, m) what is the maximal possible value the procedure may output?
Input
There are multiple test cases. The first line of input is an integer T (0 < T ≤ 100), indicating the number of test cases. Then T test cases follow. Each case starts with a line of 2 integers n and m (0 < n ≤ 200, 0 < m ≤ 10000). Then m lines of 3 integers follow. The i-th(1 ≤ i ≤ m) line of them are ai-1 ,bi-1 and ci-1 (0 ≤ ai-1, bi-1 < n, 0 ≤ ci-1 ≤ 2).
Output
For each test case, output the result in a single line.
Sample Input
3
2 1
0 1 0
2 1
0 0 0
2 2
0 1 0
1 1 2
Sample Output
1
1
2
Author
CAO, Peng
Source
2010 Asia Chengdu Regional Contest
题目大意:
给你四个数组a【】,b【】,c【】,x【】。其中a,b数组的值小于n,c数组的值范围是0,1,2。x数组的值范围是0,1
然后有一个递归函数。
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
思路:
概述:
相信做过2-sat的题目的小伙伴们,都知道2-sat题目的概述:
一共有n个党派,每个党派有两个人,每个党派之间的人会有矛盾关系,问是否能够选出n个人,使得这些人在场没有矛盾关系。
那么我们做这类题的核心思想就锁定到转化难题到这样的基础问题上,相信大家就会建边和解题了。
1、对于公式的分析:
①首先,输入进来的值是a,b,c。也就是说,对于递归的每一层的c【dep】,a【dep】,b【dep】的值都是确定的,只要我们对x【】数组找到一种合理赋值即可。而且x【】只能取0或者1,这个时候我们抽象化x【】=0为2-sat中的a,x【】=1为2-sat中的!a,这个时候,我们就可以理解为一共这个图中有n个点。那么x【i】=0的节点我们设定为i,x【i】=1的节点我们设定为i+n即可。
那么i就表示第i个党派的0号人,i+n就表示第i个党派的1号人。
②公式中c【dep】的值就是让我们寻找矛盾边的关键。
如果c【dep】==0,那么就表示x【a【dep】】和x【b【dep】】都不能为0,那么就是说,a【dep】和b【dep】有矛盾。
如果c【dep】==1,那么就表示x【a【dep】】为1的时候x【b【dep】】不能为0,同理,x【a【dep】】为0的时候,x【b【dep】】不能为1,也就是说a【dep】+n和b【dep】有矛盾,而且a【dep】和b【dep】+n有矛盾。
如果c【dep】==2,那么就表示x【a【dep】】和x【b【dep】】都不能为1,那么就是说,a【dep】+n和b【dep】+n有矛盾。
③既然找到了所有的矛盾边,那么建边问题也就解决了,对于a,b有矛盾的边,要建立对应的两条边:(a,!b)(b,!a)。我们根据这个原理,建边。
以上,我们将问题转化到了2-sat的基础问题上来,建边的问题也就搞定了。
2、对于题目解法的分析:
正常的思路应该是这样的:
①i=0
②建m=i时的边
③搞一下强连通然后判断是否符合2-sat。如果符合,跳④,如果不符合,结束算法。
④i++,跳②
然而m比较大,而且搞强连通的复杂度为n+m,然后我们对m进行枚举,总时间复杂度大概为:O((n+m)*m)。其中m==10000,必然超时。
所以这个时候我们就要优化算法:
考虑到这个问题的递增性,我们可以使用二分的算法来优化时间复杂度。
这样我们就从建一条边搞一发强连通,变成了建一堆边搞一发强连通,时间复杂度降低大概为:O((n+m)*logm)
3、实现代码,完成AC!
AC代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int head[100000];
struct EdgeNode
{
int to;
int flag;
int next;
}e[100000];
int a[10050];
int b[10050];
int c[10050];
int vis[10050];
int low[10050];
int dfn[10050];
int color[10050];
int stack[100500];
int cont,n,m,sig,tt,cnt;
void add(int from,int to)
{
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
void Tarjan(int u)
{
vis[u]=1;
stack[++tt]=u;
low[u]=dfn[u]=cnt++;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]==0)Tarjan(v);
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
color[stack[tt]]=sig;
vis[stack[tt]]=-1;
}
while(stack[tt--]!=u);
}
}
int Slove()
{
tt=-1;
sig=0;
cnt=1;
memset(stack,0,sizeof(stack));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(color,0,sizeof(color));
memset(vis,0,sizeof(vis));
for(int i=0;i<n*2;i++)
{
if(vis[i]==0)
{
Tarjan(i);
}
}
for(int i=0;i<n;i++)
{
if(color[i]==color[i+n])return 0;
}
return 1;
}
void erfen()
{
int l=0;
int r=m-1;
int mid;
while(r-l>=0)
{
mid=(l+r)/2;
cont=0;
memset(head,-1,sizeof(head));
for(int i=0;i<=mid;i++)
{
int dep=i;
if(c[i]==0)
{
add(a[dep],b[dep]+n);
add(b[dep],a[dep]+n);
}
if(c[i]==1)
{
add(a[dep],b[dep]);
add(b[dep],a[dep]);
add(a[dep]+n,b[dep]+n);
add(b[dep]+n,a[dep]+n);
}
if(c[i]==2)
{
add(a[dep]+n,b[dep]);
add(b[dep]+n,a[dep]);
}
}
if(Slove()==0)//左边
{
r=mid-1;
}
if(Slove()==1)//右边
{
l=mid+1;
}
}
printf("%d\n",l);
}
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 i;
erfen();
}
}