loj 1251(2-sat + 输出一组可行解)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26961

思路:u表示留下,~u表示离开,同理v,对于+u,-v,我们可以这样来定义:若u离开,则v必须留下,如v离开,则u必须留下,于是我们可以连边u+n->v,v+n->u,后面的同理。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<stack>
  6 #include<vector>
  7 #include<queue>
  8 using namespace std;
  9 #define MAXN 20000
 10 #define MAXM 444444
 11 
 12 struct Edge{
 13     int v,next;
 14 }edge[MAXM];
 15 
 16 int n,m,NE;
 17 int head[MAXN];
 18 
 19 void Insert(int u,int v)
 20 {
 21     edge[NE].v=v;
 22     edge[NE].next=head[u];
 23     head[u]=NE++;
 24 }
 25 
 26 int cnt,bcc_count;
 27 int low[MAXN],dfn[MAXN],color[MAXN];
 28 bool mark[MAXN];
 29 stack<int>S;
 30 
 31 void Tarjan(int u)
 32 {
 33     low[u]=dfn[u]=++cnt;
 34     mark[u]=true;
 35     S.push(u);
 36     for(int i=head[u];i!=-1;i=edge[i].next){
 37         int v=edge[i].v;
 38         if(dfn[v]==0){
 39             Tarjan(v);
 40             low[u]=min(low[u],low[v]);
 41         }else if(mark[v]){
 42             low[u]=min(low[u],dfn[v]);
 43         }
 44     }
 45     if(low[u]==dfn[u]){
 46         int v;
 47         bcc_count++;
 48         do{
 49             v=S.top();
 50             S.pop();
 51             mark[v]=false;
 52             color[v]=bcc_count;
 53         }while(u!=v);
 54     }
 55 }
 56 
 57 int opp[MAXN];
 58 bool Judge()
 59 {
 60     for(int i=1;i<=n;i++){
 61         if(color[i]==color[i+n])return false;
 62         //opp[x]保存的是和编号为x的连通分量矛盾的连通分量的编号
 63         opp[color[i]]=color[i+n];
 64         opp[color[i+n]]=color[i];
 65     }
 66     return true;
 67 }
 68 
 69 vector<vector<int> >g;
 70 vector<int>ans;
 71 int degree[MAXN];
 72 int vis[MAXN];
 73 
 74 void TopSort()
 75 {
 76     queue<int>que;
 77     for(int i=1;i<=bcc_count;i++){
 78         if(degree[i]==0)que.push(i);
 79     }
 80     while(!que.empty()){
 81         int u=que.front();
 82         que.pop();
 83         if(vis[u]==0){
 84             //染色
 85             vis[u]=1;
 86             vis[opp[u]]=-1;
 87         }
 88         for(int i=0;i<g[u].size();i++){
 89             degree[g[u][i]]--;
 90             if(degree[g[u][i]]==0)que.push(g[u][i]);
 91         }
 92     }
 93 }
 94 
 95 
 96 int main()
 97 {
 98     int _case,t=1,a,b,u,v;
 99     scanf("%d",&_case);
100     while(_case--){
101         scanf("%d %d",&m,&n);
102         NE=0;
103         memset(head,-1,sizeof(head));
104         while(m--){
105             scanf("%d%d",&a,&b);
106             u=abs(a),v=abs(b);
107             if(a>0&&b>0)Insert(u+n,v),Insert(v+n,u);
108             else if(a>0&&b<0)Insert(u+n,v+n),Insert(v,u);
109             else if(a<0&&b>0)Insert(u,v),Insert(v+n,u+n);
110             else Insert(u,v+n),Insert(v,u+n);
111         }
112         cnt=bcc_count=0;
113         memset(mark,false,sizeof(mark));
114         memset(dfn,0,sizeof(dfn));
115         memset(color,0,sizeof(color));
116         for(int i=1;i<=2*n;i++)if(dfn[i]==0)Tarjan(i);
117         printf("Case %d: ",t++);
118         if(!Judge()){
119             puts("No");
120             continue;
121         }
122         puts("Yes");
123         //反向建图
124         memset(degree,0,sizeof(degree));
125         g.clear();
126         g.resize(2*n+2);
127         for(int u=1;u<=2*n;u++){
128             for(int i=head[u];i!=-1;i=edge[i].next){
129                 int v=edge[i].v;
130                 if(color[u]!=color[v]){
131                     g[color[v]].push_back(color[u]);
132                     degree[color[u]]++;
133                 }
134             }
135         }
136         memset(vis,0,sizeof(vis));
137         TopSort();
138         ans.clear();
139         for(int i=1;i<=n;i++){
140             if(vis[color[i]]==1)ans.push_back(i);
141         }
142         printf("%d",(int)ans.size());
143         sort(ans.begin(),ans.end());
144         for(int i=0;i<(int)ans.size();i++){
145             printf(" %d",ans[i]);
146         }
147         puts("");
148      }
149     return 0;
150 }
View Code

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值