2-SAT问题模板
先看两道例题:
在搞懂上面的题后,这题就并不难了。
最主要的就是跑tarjan和连边。
比如说:a,b为一个党派,c,d为一个党派,且a,c有仇,那么只能选a,d或b,c。也就是说要在a,d之间连一条边,在b,c之间连一条边。
上述操作完成后,tarjan跑一边缩点,如果同一个党派的两人在同一个强连通分量里,那么和平委员会就不能创立。~~(别告诉我你连tarjan都不会)~~
代码如下
邻接表做法:
#include<bits/stdc++.h>
using namespace std;
vector<int>g[16005];
stack<int>s;
int n,m,a,b,ind,cnt,dfn[16005],low[16005],scc[16005],opp[16005],val[16005];
bool f,vis[16005];
void tarjan(int u){
dfn[u]=low[u]=++ind;
s.push(u);
vis[u]=1;
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int v;
do{
v=s.top();
s.pop();
scc[v]=cnt;
vis[v]=0;
}while(u!=v);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b&1?b+1:b-1);
g[b].push_back(a&1?a+1:a-1);
}
for(int i=1;i<=n*2;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=n*2;i+=2){
if(scc[i]==scc[i+1]){
f=1;
break;
}
}
if(f)printf("NIE");
else for(int i=1;i<=2*n;i++)if(scc[i]<scc[i&1?i+1:i-1])printf("%d
",i);
}
前向星做法:
#include<bits/stdc++.h>
using namespace std;
struct edge{
int next,to;
}ed[32005];
stack<int>s;
int n,m,a,b,num,head[16005],ind,cnt,dfn[16005],low[16005],scc[16005],opp[16005],val[16005];
bool f,vis[16005];
void add(int u,int v){
num++;
ed[num].to=v;
ed[num].next=head[u];
head[u]=num;
}
void tarjan(int u){
dfn[u]=low[u]=++ind;
s.push(u);
vis[u]=1;
for(int i=head[u];i;i=ed[i].next){
int v=ed[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int v;
do{
v=s.top();
s.pop();
scc[v]=cnt;
vis[v]=0;
}while(u!=v);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
add(a,b&1?b+1:b-1);
add(b,a&1?a+1:a-1);
}
for(int i=1;i<=n*2;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=n*2;i+=2){
if(scc[i]==scc[i+1]){
f=1;
break;
}
}
if(f)printf("NIE");
else for(int i=1;i<=2*n;i++)if(scc[i]<scc[i&1?i+1:i-1])printf("%d
",i);
}
对比:邻接表占空间少,但比较慢;前向星占空间多,但比较快。反正都能过