BZOJ 1997: [Hnoi2010]Planar|2-SAT|二分图染色

该博客探讨了BZOJ 1997题目的解决方案,通过二分图染色和2-SAT两种方法进行建模。博主指出,尽管两种方法都能解决这个问题,但它们分别从不同的角度出发——二分图染色关注矛盾关系,而2-SAT寻找“必须”的关系。2-SAT的解法利用环内边相交推出环外边也相交的逻辑,通过建立特定边连接并运用Tarjan算法判断。二分图染色则通过寻找矛盾关系来解决问题。代码实现中,2-SAT方法的时间复杂度与二分图染色相同,但在常数上略有优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一道比较奇怪的题

用二分图染色和2-SAT都可以建模

想了一下为什么

二分图染色是找出矛盾的关系连边

2-SAT是找出“必须”的关系连边

而此题恰好都满足

--------------------------------------------------

2-SAT做法:

一组边a,b在环构成的圆内相交 那么在圆外的a‘,b'也相交

”必须“关系有 a-->b'   a'-->b   b-->a‘  b'-->a

直接连边就可以了然后tarjan判断

--------------------------------------------------

二分图染色做法:

引用上面的a,b,a',b'

矛盾关系就是   a-->a'   b-->b'   a-->b   a'-->b'

两种算法的复杂度是一样的然后用二分图染色的话,常数会稍小点

下面贴代码

2-SAT   124ms

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
using namespace std;
int sc()
{
	int i=0; char c=getchar();
	while( c>'9' || c<'0' ) c=getchar();
	while( c>='0'&& c<='9') i=i*10+c-'0',c=getchar();
	return i;
}
int u[11111],v[11111];
int head[1888],nxt[1222222],lst[1222222];
int dfn[1888],low[1888],st[1888],vis[1888],bl[1888];
int num[222],pos[222];
int n,m,total,tot,cnt,scc,top;
void insert(int x,int y)
{
	lst[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void tarjan(int x)
{
	vis[x]=1; st[++top]=x;
	dfn[x]=low[x]=++cnt;
	for(int i=head[x]; i; i=nxt[i])
	{
		if(vis[lst[i]]) low[x]=min(low[x],dfn[lst[i]]);
		else if(!dfn[lst[i]])
		{
			tarjan(lst[i]);
			low[x]=min(low[x],low[lst[i]]);
		}
	}
	if(dfn[x]==low[x])
	{
		int k=0; scc++;
		while(k!=x)
		{
			k=st[top--];
			bl[k]=scc;
			vis[k]=0;
		}
	}
}
int main()
{
	int Q=sc();
	while(Q--)
	{
		memset(head,0,sizeof(head));
		memset(dfn,0,sizeof(dfn));
		int flag=0; total=tot=cnt=top=scc=0;
		n=sc(); m=sc();
		for(int i=1; i<=m; i++) u[i]=sc(),v[i]=sc();
		for(int i=1; i<=n; i++) num[i]=sc(),pos[num[i]]=i;
		if(m>3*n-6) {puts("NO");continue;}
		for(int i=1; i<=m; i++)
		{
			u[i]=pos[u[i]],v[i]=pos[v[i]];
			if(u[i]>v[i]) swap(u[i],v[i]);
			if(v[i]-u[i]==1||v[i]-u[i]==n-1) continue;
			u[++total]=u[i],v[total]=v[i];
		}
		for(int i=1; i<=total; i++)
		    for(int j=i+1; j<=total; j++)
		    if((u[i]<u[j]&&u[j]<v[i]&&v[i]<v[j])||(u[j]<u[i]&&u[i]<v[j]&&v[j]<v[i]))
		    {
		    	insert(2*i,2*j+1);
		    	insert(2*i+1,2*j);
		    	insert(2*j,2*i+1);
		    	insert(2*j+1,2*i);
		    }
		for(int i=2; i<=2*total+1; i++)
		    if(!dfn[i]) tarjan(i);
		for(int i=1; i<=total; i++)
		    if(bl[i*2]==bl[i*2+1]){flag=1,puts("NO");break;}
		if(!flag) puts("YES");
	}
	return 0;
}

二分图染色   100ms

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<iostream>
using namespace std;
int sc()
{
	int i=0; char c=getchar();
	while( c>'9' || c<'0' ) c=getchar();
	while( c>='0'&& c<='9') i=i*10+c-'0',c=getchar();
	return i;
}
int u[11111],v[11111];
int head[1888],nxt[1222222],lst[1222222];
int col[1888];
int num[222],pos[222];
int n,m,total,tot;
void insert(int x,int y)
{
	lst[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
bool color(int x,int f)
{
	col[x]=f;
	for(int i=head[x]; i; i=nxt[i])
	{
		if(!col[lst[i]])
		{
			if(!color(lst[i],f^1)) return 0;
		}
		else  if(col[lst[i]]==col[x]) return 0;
	}
	return 1;
}	
int main()
{
	int Q=sc();
	while(Q--)
	{
		memset(head,0,sizeof(head));
		memset(col,0,sizeof(col));
		int flag=0; total=tot=0;
		n=sc(); m=sc();
		for(int i=1; i<=m; i++) u[i]=sc(),v[i]=sc();
		for(int i=1; i<=n; i++) num[i]=sc(),pos[num[i]]=i;
		if(m>3*n-6) {puts("NO");continue;}
		for(int i=1; i<=m; i++)
		{
			u[i]=pos[u[i]],v[i]=pos[v[i]];
			if(u[i]>v[i]) swap(u[i],v[i]);
			if(v[i]-u[i]==1||v[i]-u[i]==n-1) continue;
			u[++total]=u[i],v[total]=v[i];
		}
		for(int i=1; i<=total; i++)
		    for(int j=i+1; j<=total; j++)
		    if((u[i]<u[j]&&u[j]<v[i]&&v[i]<v[j])||(u[j]<u[i]&&u[i]<v[j]&&v[j]<v[i]))
		    {
		    	insert(2*i,2*j);
		    	insert(2*j,2*i);
		    	insert(2*i+1,2*j+1);
		    	insert(2*j+1,2*i+1);
		    }
		for(int i=1; i<=total; i++) insert(2*i,2*i+1),insert(2*i+1,2*i);
		for(int i=2; i<=total*2+1; i++)
		    if(!col[i]&&!color(i,2)){flag=1;break;}
		if(flag)puts("NO");else puts("YES");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值