题意
要举行一个会议,有i个组织,每个组织中有2个人,编号为2i-1和2i,有仇的两个人不能同时参与会议,求解字典序最小的安排方案
思路
很明显的2-SAT问题
利用连边表示$a \rightarrow b$的关系。
若a,b有仇,则有a->b’和b->a’
然后利用dfs,从小的编号开始计算即可
代码
1 2 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”
- 边要开双倍空间