您的位置:首页 > 编程语言 > Go语言

Let's go home(POJ 1824)---2-SAT模板题

2020-06-22 04:33 441 查看

题目链接

题目描述

小时候,乡愁是一枚小小的邮票,我在这头,母亲在那头。
—— 余光中
集训是辛苦的,道路是坎坷的,休息还是必须的。经过一段时间的训练,lcy决定让大家回家放松一下,但是训练还是得照常进行,lcy想出了如下回家规定,每一个队(三人一队)或者队长留下或者其余两名队员同时留下;每一对队员,如果队员A留下,则队员B必须回家休息下,或者B留下,A回家。由于今年集训队人数突破往年同期最高记录,管理难度相当大,lcy也不知道自己的决定是否可行,所以这个难题就交给你了,呵呵,好处嘛~,免费**漂流一日。

输入格式

第一行有两个整数,T和M,1<=T<=1000表示队伍数,1<=M<=5000表示对数。
接下来有T行,每行三个整数,表示一个队的队员编号,第一个队员就是该队队长。
然后有M行,每行两个整数,表示一对队员的编号。
每个队员只属于一个队。队员编号从0开始。

输出格式

可行输出yes,否则输出no,以EOF为结束。

输入样例

1 2
0 1 2
0 1
1 2

2 4
0 1 2
3 4 5
0 3
0 4
1 3
1 4

输出样例

yes
no

分析

思路:2-SAT问题,根据题意要满足两种条件:

1)队长留 或 两个队员留

2) M 条队员 a、b 的冲突条件

假设 a、b、c 三个组成一队,a 是队长,那么由条件 1 可知 队长 a 与队员 b、c 二者只能选一种,假设走为1留为0,则一共要建立12条边:

  • a走,b不走:<a,b+*3n>
  • a走,c不走:<a,c+*3n>
  • a不走,b走:<a+3*n,b>
  • a不走,c走:<a+3*n,c>
  • b走,a不走:<b,a+*3n>
  • c走,a不走:<c,a+*3n>
  • b不走,a走:<b+3*n,a>
  • c不走,a走:<c+3*n,a>
  • b走,c走:<b,c>
  • c走,b走:<c,b>
  • b不走,c不走:<b+3*n,c+*3n>
  • c不走,b不走:<c+3*n,b+*3n>

对于 M 条冲突条件:

  • a 留,导致 b 走:<a,b+3*n>
  • b 留,导致 a 走:<b,a+3*n>

根据以上关系,添加关系至 2-SAT 中判断即可,对于2-SAT不懂的可以参考这篇博文传送门

源程序

#include <bits/stdc++.h>
#define MAXN 1005*6
#define MAXM 1005*12+2*5005
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
int v,next;
Edge(){}
Edge(int v,int next):v(v),next(next){}
}edge[MAXM];
int EdgeCount,head[MAXN];
int n,m,block_cnt,scc_cnt;
int dfn[MAXN],low[MAXN],scc[MAXN];
bool vis[MAXN];
stack<int> s;
void addEdge(int u,int v)
{
edge[++EdgeCount]=Edge(v,head[u]);
head[u]=EdgeCount;
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(vis,false,sizeof(vis));
EdgeCount=block_cnt=scc_cnt=0;
}
void tarjan(int u)
{
dfn[u]=low[u]=++block_cnt;	//时间戳
vis[u]=true;	//标记入栈
s.push(u);
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(!dfn[v]){	//还没访问过
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])	//访问过且还在栈中
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){	//满足强连通分量
scc_cnt++;
while(1){
int tmp=s.top();s.pop();
scc[tmp]=scc_cnt;	//标记所属强连通分量
vis[tmp]=false;	//标记出栈
if(tmp==u) break;
}
}
}
bool TwoSat()
{
for(int i=0;i<6*n;i++)	//缩点
if(!dfn[i])
tarjan(i);
for(int i=0;i<3*n;i++)
if(scc[i]==scc[i+3*n])	//矛盾
return false;
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
init();		//初始化
for(int i=1;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
addEdge(a,b+3*n);	//a走,b不走
addEdge(a,c+3*n);	//a走,c不走
addEdge(a+3*n,b);	//a不走,b走
addEdge(a+3*n,c);	//a不走,c走
addEdge(b,a+3*n);	//b走a,不走
addEdge(c,a+3*n);	//c走a,不走
addEdge(b+3*n,a);	//b不走,a走
addEdge(c+3*n,a);	//c不走,a走
addEdge(b,c);	//b走,c走
addEdge(c,b);	//c走,b走
addEdge(b+3*n,c+3*n);	//b不走,c不走
addEdge(c+3*n,b+3*n);	//c不走,b不走
}
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
addEdge(a,b+3*n);	//a走b不走
addEdge(b,a+3*n);	//b走a不走
}
if(TwoSat())
printf("yes\n");
else
printf("no\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: