题目描述
题解
经典的2-SAT问题,要求输出字典序最小的解
首先贪心地选最小的,每一次选择将其影响的点都修改,然后判断是否合法
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 40005
int n,m,x,y,top;
int tot,point[N],nxt[N],v[N];
int stack[N],flag[N],ans[N];
bool can,is_end;
void clear()
{
x=y=tot=can=is_end=0;
memset(point,0,sizeof(point));memset(nxt,0,sizeof(nxt));memset(v,0,sizeof(v));
memset(flag,0,sizeof(flag));memset(ans,0,sizeof(ans));
}
void add(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int x)
{
if (flag[x]==-1) {can=false;return;}
if (flag[x]) return;
flag[x]=1;flag[((x-1)^1)+1]=-1;
stack[++top]=x;
for (int i=point[x];i&&can;i=nxt[i])
{
if (!flag[v[i]]) dfs(v[i]);
else if (flag[v[i]]==-1) can=false;
}
}
int main()
{
while (~scanf("%d%d",&n,&m))
{
clear();
for (int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
add(x,((y-1)^1)+1);
add(y,((x-1)^1)+1);
}
for (int i=1;i<=2*n;i+=2)
{
if (flag[i]&&flag[i+1])
{
if (flag[i]==flag[i+1]) {is_end=true;break;}
if (flag[i]==1) ans[i/2+1]=i;
else ans[i/2+1]=i+1;
continue;
}
top=0;
can=true;dfs(i);
if (can) {ans[i/2+1]=i;continue;}
else
for (int j=1;j<=top;++j)
flag[stack[j]]=flag[((stack[j]-1)^1)+1]=0;
top=0;
can=true;dfs(i+1);
if (can) {ans[i/2+1]=i+1;continue;}
else
for (int j=1;j<=top;++j)
flag[stack[j]]=flag[((stack[j]-1)^1)+1]=0;
is_end=true;break;
}
if (is_end) {puts("NIE");continue;}
for (int i=1;i<=n;++i) printf("%d\n",ans[i]);
}
}