题意
要举行一个会议,有i个组织,每个组织中有2个人,编号为2i-1和2i,有仇的两个人不能同时参与会议,求解字典序最小的安排方案
思路
很明显的2-SAT问题
利用连边表示$a \rightarrow b$的关系。
若a,b有仇,则有a->b’和b->a’
然后利用dfs,从小的编号开始计算即可
代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 
 | #include <cstdio>#include <cstring>
 #include <algorithm>
 #include <stack>
 using namespace std;
 int n,m;
 int fir[20200],nxt[20200*2],u[20200*2],v[20200*2],cnt;
 int rev[20200];
 void addedge(int ui,int vi){
 ++cnt;
 u[cnt]=ui;
 v[cnt]=vi;
 nxt[cnt]=fir[ui];
 fir[ui]=cnt;
 }
 int S[20200],top=0;
 int mark[20200];
 bool dfs(int u){
 if(mark[rev[u]])
 return false;
 if(mark[u])
 return true;
 mark[u]=1;
 S[++top]=u;
 for(int i=fir[u];i;i=nxt[i]){
 if(!dfs(v[i]))
 return false;
 }
 return true;
 }
 void solve(void){
 for(int i=1;i<=m;i++){
 int a,b;
 scanf("%d %d",&a,&b);
 
 addedge(a,rev[b]);
 addedge(b,rev[a]);
 }
 for(int i=1;i<=n;i++){
 if(!mark[2*i-1]&&!mark[2*i]){
 top=0;
 if(!dfs(2*i-1)){
 while(top){
 mark[S[top]]=0;
 top--;
 }
 if(!dfs(2*i)){
 printf("NIE\n");
 return;
 }
 }
 }
 }
 for(int i=1;i<=2*n;i++){
 if(mark[i]){
 printf("%d\n",i);
 }
 }
 }
 int main(){
 for(int i=1;i<=10000;i++){
 rev[2*i]=2*i-1;
 rev[2*i-1]=2*i;
 }
 while(scanf("%d %d",&n,&m)==2){
 solve();
 for(int i=1;i<=2*n;i++){
 mark[i]=0;
 fir[i]=0;
 }
 for(int i=1;i<=cnt;i++){
 u[i]=v[i]=nxt[i]=0;
 }
 cnt=0;
 }
 return 0;
 }
 
 | 
注意事项
- 多组数据,记得清空
- 无解输出”NIE”而不是”NO”
- 边要开双倍空间