POJ 3683 【2-sat+求一组可行解】.cpp

题意:

  有一个牧师要给好几对新婚夫妇准备婚礼..

  已知每对新婚夫妇的有空的时间以及婚礼持续时间..

  问是否可以让每对新婚夫妇都得到该牧师的祝福~

  如果可以就输出YES以及可行解 不可以就输出NO

输入:

  一个n 表示有n对新婚夫妇

  接下来n行每行a b c 表示在a~b这段时间有空..以及婚礼会持续 c..

  P.S.时间只可以选在a开始或者b结束..

  eg:08:00 09:00 30 可以在8:00~8:30举行婚礼或者8:30~9:00举行婚礼

 

思路:

  2-sat 以在前一段时间举行婚礼或者后一段时间举行婚礼为2个可选方案

  然后每对新婚夫妇婚礼时间冲突了就给和另外一个方案连线..

  2-sat问题连边的要求就是这两个情况一定要同时发生..

  然后根据建的图求强连通分量以及缩点..表示是否可以同时发生并方便后面拓扑排序..

  总方案不成立的条件是一个强连通分量里有col[ i ] == col[ i+n ]

 

  而找可行解的方案就是:

  根据第一次建的图建一个反图..然后求逆拓扑排序..

  建反图的原因是保持冲突的两个事件肯定会被染成不同的颜色..

  求逆拓扑排序的原因也是为了对图染的色不会发生冲突..

 

  输出可行解就是遍历一次逆拓扑排序时染成的颜色..

  输出同一组颜色的解就是其中的一组可行解..

  

Tips:

  ※ 用双向链表..方便建反图..

  ※ 求强连通分量是把所有的点都遍历一遍..所以是2*n..

  ※ 建反图的时候是遍历所有的边..然后根据edge.from 和 edge.to 建图

  ※ 拓扑排序是对缩点后的有向无环图排序..找出一组可行解..

    每次拓扑排序找到一个解的时候..就把和他冲突的另外一个解也染色了..

  ※ 连边的时候连的是必须发生的两件事..

  ※ 如果求连通分量染色的时候遇到冲突事件在一个分量中出现则不能有可行解

     否则就把缩点后冲突事件记录起来..以便拓扑排序的时候染色..

  ※ 最后记录某一个颜色的可行解..

    然后遍历一遍1~n 找出该时间是否属于该可行解..属于就输出..不属于就输出对立的解..

 

总结:

 

  2-sat问题是
  ①. 把有冲突的分成n组
  ②. 每组2个..然后根据冲突关系把没冲突的连边
  ③. 用tarjan染色..
  ④. 把缩点后的图建反图..
  ⑤. 根据反图拓扑排序
  ⑥. 拓扑排序过程中给其中一组解染色..同时给冲突的解染色
  ⑦. 根据第⑥.步找出可行解并输出..

 

Code:

View Code
  1 #include <stdio.h>
  2 #include <cstring>
  3 #include <fstream>
  4 #include <algorithm>
  5 using namespace std;
  6 #define clr(x) memset(x, 0, sizeof(x))
  7 const int INF = 0x1f1f1f1f;
  8 const int MAXN = 1010*2;
  9 
 10 struct Time
 11 {
 12     int st;
 13     int en;
 14     int la;
 15 }time[MAXN];
 16 
 17 struct Edge
 18 {
 19     int from;
 20     int next;
 21     int to;
 22 }edge[3000010], edge2[3000010];///!!!
 23 int head[MAXN], head2[MAXN];
 24 int tot, tot2;
 25 
 26 void add(int s, int u)
 27 {
 28     edge[tot].from = s;
 29     edge[tot].to = u;
 30     edge[tot].next = head[s];
 31     head[s] = tot++;
 32 }
 33 
 34 void add2(int s, int u)
 35 {
 36     edge2[tot2].to = u;
 37     edge2[tot2].next = head2[s];
 38     head2[s] = tot2++;
 39 }
 40 
 41 int low[MAXN], dfn[MAXN];
 42 int ins[MAXN], sta[MAXN], col[MAXN];
 43 int ti, top, cnt;
 44 
 45 void tarjan(int u)
 46 {
 47     int i, j, k;
 48     dfn[u] = low[u] = ++ti;
 49     sta[++top] = u;
 50     ins[u] = 1;
 51     for(i = head[u]; i != -1; i = edge[i].next) {
 52         k = edge[i].to;
 53         if(!dfn[k]) {
 54             tarjan(k);
 55             low[u] = min(low[u], low[k]);
 56         } else if(ins[k])
 57             low[u] = min(low[u], dfn[k]);
 58     }
 59     if(dfn[u] == low[u]) {
 60         cnt++;
 61         do
 62         {
 63             k = sta[top--];
 64             col[k] = cnt;
 65             ins[k] = 0;
 66         }while(k != u);
 67     }
 68 }
 69 
 70 int n;
 71 void solve_ta()
 72 {
 73     int i, j, k;
 74     ti = cnt = top = 0;
 75     clr(dfn);
 76     for(i = 1; i <= 2*n; ++i)
 77     if(!dfn[i])
 78         tarjan(i);
 79 }
 80 
 81 int ind[MAXN], res[MAXN];
 82 int q[MAXN];
 83 
 84 void make_G()
 85 {
 86     int i, j, k;
 87     tot2 = 0;
 88     memset(head2, 0xff, sizeof(head2));
 89     clr(ind);
 90     for(i = 0; i < tot; ++i) {///!!!!
 91         if(col[edge[i].from] != col[edge[i].to]) {
 92             add2(col[edge[i].to], col[edge[i].from]);
 93             ind[col[edge[i].from]]++;
 94         }
 95     }
 96 }
 97 
 98 int ccol[MAXN];
 99 int ans[MAXN], ct[MAXN];
100 void toposort()
101 {
102     int front = 0, rear = 0;
103     int i, k;
104     make_G();
105     top = 0;
106     clr(ccol);
107     for(i = 1; i <= cnt; i++)///!!
108         if(ind[i] == 0)
109             q[rear++] = i;
110     while(front < rear) {
111         int x = q[front++];
112         if(ccol[x] == 0) {
113             ccol[x] = 1;
114             ccol[ct[x]] = -1;
115         }
116         for(i = head2[x]; i != -1;i = edge2[i].next) {
117             k = edge2[i].to;
118             ind[k]--;
119             if(ind[k] == 0) {
120                 q[rear++] = k;
121             }
122         }
123     }
124 }
125 
126 int main()
127 {
128     int i, j, k;
129     int sh, sm, eh, em, d;///!!!
130     bool flag;
131    // freopen("e:\\acm\\mess\\stdin-stdout\\in.txt", "r", stdin);
132     while(scanf("%d", &n) != EOF)
133     {
134         tot = 0;
135         memset(head, 0xff, sizeof(head));
136         flag = false;
137 
138         for(i = 1; i <= n; ++i) {
139             scanf("%d:%d %d:%d %d", &sh, &sm, &eh, &em, &d);
140             time[i].st = sh*60+sm;
141             time[i].en = eh*60+em;
142             time[i].la = d;
143         }
144 
145         for(i = 1; i <= n; ++i)
146         for(j = 1; j <= n; ++j)
147             if(i != j) {
148                 /*
149                 if((time[i].st+time[i].la < time[j].st) || (time[i].st > time[j].st + time[j].la))
150                     add(i, j);
151                 if((time[i].en-time[i].la > time[j].st+time[j].la) || (time[i].en < time[j].st))
152                     add(i+n, j);
153                 if((time[i].st+time[i].la < time[j].en-time[j].la) || (time[i].st > time[j].en))
154                     add(i, j+n);
155                 if((time[i].en-time[i].la > time[j].en) || (time[i].en < time[j].en-time[j].la))
156                     add(i+n, j+n);
157                 */
158 
159                 if(time[i].st < time[j].st+time[j].la && time[j].st < time[i].st+time[i].la )
160                     add(i, j+n);
161                 if(time[i].st < time[j].en && time[j].en-time[j].la < time[i].st+time[i].la)
162                     add(i, j);
163                 if(time[i].en-time[i].la< time[j].st+time[j].la && time[j].st<time[i].en)
164                     add(i+n, j+n);
165                 if(time[i].en - time[i].la < time[j].en && time[j].en-time[j].la < time[i].en)
166                     add(i+n, j);
167             }
168 
169         solve_ta();
170         for(i = 1; i <= n; ++i) {
171             if(col[i] == col[i+n]) {
172                 flag = true;
173                 break;
174             }
175             ct[col[i]] = col[i+n];
176             ct[col[i+n]] = col[i];
177         }
178 
179 
180         if(flag) puts("NO");
181         else {
182             toposort();
183             clr(ans);
184 
185             for(i = 1; i <= 2*n; ++i)
186                 if(ccol[col[i]] == 1)
187                     ans[i] = 1;
188             puts("YES");
189             for(i = 1; i <= n; ++i) {
190                 if(ans[i]) printf("%02d:%02d %02d:%02d\n", time[i].st/60, time[i].st%60, (time[i].st+time[i].la)/60, (time[i].st+time[i].la)%60);
191                 else printf("%02d:%02d %02d:%02d\n", (time[i].en-time[i].la)/60, (time[i].en-time[i].la)%60, time[i].en/60, time[i].en%60);
192             }
193         }
194 
195     }
196     return 0;
197 }

 

 

题目链接:http://poj.org/problem?id=3683

转载于:https://www.cnblogs.com/Griselda/archive/2012/10/09/2716647.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值