【图论】2018国庆三校联考D5T2

【图论】2018国庆三校联考D5T2

分析:

题意非常丑陋。。。简化出来就一句话:每个点有选中、未选中两种状态,现在给出一些矛盾关系,要求加入尽可能少的矛盾关系,使得没有合法方案。

如此2sat的模型,显然需要2sat的连边方式。。。然后直接枚举每个位置选、不选是否合法即可。若不选合法,则考虑其练的边是否有一个选择的点,如果有则可以用1的代价使得这个点不选非法。具体的情况见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 4010
using namespace std;
struct node{
	int x;
	node *nxt;	
}edge[MAXN*4];
node *head[MAXN],*ncnt=edge;
int n,m;
void add_edge(int u,int v){
	ncnt++;
	ncnt->x=v;
	ncnt->nxt=head[u];
	head[u]=ncnt;
}
bool vis[MAXN];
void dfs(int x){
	vis[x]=1;
	for(node *v=head[x];v!=NULL;v=v->nxt){
		int u=v->x;
		if(vis[u]==0)
			dfs(u);
	}
}
int query(int x){
	memset(vis,0,sizeof vis);
	dfs(x);	
	for(int i=1;i<=n;i++)
		if(vis[i]==1&&vis[i+n]==1)
			return 0;
	return 1;
}
int check(){
	for(int i=1;i<=n;i++)
		if(vis[i]==1)
			return 1;
	return 0;	
}
int l[MAXN];
int main(){
	freopen("god.in","r",stdin);
	freopen("god.out","w",stdout);
	int t;
	SF("%d",&t);
	while(t--){
		SF("%d%d",&n,&m);
		for(int i=1;i<=n*2;i++)
			head[i]=NULL;
		ncnt=edge;
		int ans=3,u,v;
		for(int i=1;i<=m;i++){
			SF("%d%d",&u,&v);
			if(u<0)
				u=-u+n;
			if(v<0)
				v=-v+n;
			add_edge(u,(v+n)%(2*n));
			add_edge(v,(u+n)%(2*n));
		}
		for(int i=1;i<=n;i++){
			int res1=query(i);
			int res2=query(i+n);
			int con=check();
			if(res2==0)
				ans=min(ans,res1);
			else if(con==1)
				ans=min(ans,1+res1);
		}
		if(ans==3)
			PF("No Way\n");	
		else
			PF("%d\n",ans);
	}
}