poj 3683(2-SAT+SCC)

传送门:Problem 3683

 https://www.cnblogs.com/violet-acmer/p/9769406.html

参考资料:

  [1]:挑战程序设计竞赛

题意:

  有n场婚礼,每场婚礼有起始时间si,结束时间ti,还有一个主持仪式需要花费ti时间,ti必须安排在婚礼的开始或者结束。

  主持由神父来做,但是只有一个神父,所以各个婚礼的主持时间不能重复,不过神父可以在出席完一个主持仪式后,立刻出席另一个开始时间与其结束时间相等的主持仪式,问你有没有可能正常的安排主持时间,不能输出no,能的话要输出具体的答案:即每个婚礼的主持时间在那个时间段。

题解:

  对于每个结婚仪式 i ,只有在开始或结束时进行主持仪式,两种选择,因此可定义变量 Xi;

      Xi 为真 <=> 在开始时进行主持仪式

  这样,对于婚礼 i 和 j ,如果Si~Si+Di 和 Sj~Sj+Dj 冲突,就有子句 (非Xi V 非Xj) 为真,对于开始和开始,开始和结束,结束和开始,结束和结束分别对应四条子句,也可得到类似的条件。

  于是,要保证所有主持仪式的时间不冲突,只要考虑将这些所有的子句用 ^(合取)连接起来所得道德布尔公式就好了。

  例如,对于输入的样例可以得到的布尔公式为:

                                            

  而当x1为真而x2为假时,其值为真。

  这样,我们就把原问题转为了2-SAT问题,接下来只要进行强连通分量分解并判断是否有使得布尔公式值为真的一组布尔变量就好了。

  以上分析来自挑战程序设计竞赛P326。

难点:  

  根据2-SAT建立有向图,用SCC分解强连通分量

AC代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<vector>
  4 #include<cstring>
  5 #include<cmath>
  6 using namespace std;
  7 #define mem(a,b) (memset(a,b,sizeof a))
  8 #define pb push_back
  9 const int maxn=1e3+50;
 10 
 11 //X1~Xn : 1~n
 12 //非X1~非Xn : n+1~2*n
 13 int n;
 14 int s[maxn];
 15 int t[maxn];
 16 int d[maxn];
 17 vector<int >G[2*maxn],rG[2*maxn];//注意此处是 2*maxn
 18 vector<int >vs;
 19 int scc[2*maxn];
 20 bool vis[2*maxn];
 21 void addEdge(int u,int v)
 22 {
 23     G[u].pb(v);
 24     rG[v].pb(u);
 25 }
 26 void SAT()
 27 {
 28     for(int i=1;i <= n;++i)
 29     {
 30         for(int j=1;j < i;++j)
 31         {
 32             if(max(s[i],s[j]) < min(s[j]+d[j],s[i]+d[i]))//第 i 场婚礼的开始时主持时间与第 j 场婚礼的开始时主持时间冲突
 33                 addEdge(i,j+n),addEdge(j,i+n);
 34             if(max(s[i],t[j]-d[j]) < min(t[j],s[i]+d[i]))//第 i 场婚礼的开始时主持时间与第 j 场婚礼的结束时主持时间冲突
 35                 addEdge(i,j),addEdge(j+n,i+n);//注意,第二处是 j+n -> i+n
 36             if(max(s[j],t[i]-d[i]) < min(t[i],s[j]+d[j]))//第 i 场婚礼的结束时主持时间与第 j 场婚礼的开始时主持时间冲突
 37                 addEdge(i,j),addEdge(i+n,j+n);//注意,第二处是 i+n -> j+n
 38             if(max(t[i]-d[i],t[j]-d[j]) < min(t[i],t[j]))//第 i 场婚礼的结束时主持时间与第 j 场婚礼的结束时主持时间冲突
 39                 addEdge(i+n,j),addEdge(j+n,i);
 40         }
 41     }
 42 }
 43 void Dfs(int u)
 44 {
 45     vis[u]=true;
 46     for(int i=0;i < G[u].size();++i)
 47         if(!vis[G[u][i]])
 48             Dfs(G[u][i]);
 49     vs.pb(u);
 50 }
 51 void rDfs(int u,int k)
 52 {
 53     vis[u]=true;
 54     scc[u]=k;
 55     for(int i=0;i < rG[u].size();++i)
 56     {
 57         int to=rG[u][i];
 58         if(!vis[to])
 59             rDfs(to,k);
 60     }
 61 }
 62 void SCC()
 63 {
 64     mem(vis,false);
 65     vs.clear();
 66     for(int i=1;i <= n;++i)
 67         if(!vis[i])
 68             Dfs(i);
 69     mem(vis,false);
 70     int k=0;
 71     for(int i=vs.size()-1;i >= 0;--i)
 72         if(!vis[vs[i]])
 73             rDfs(vs[i],++k);
 74 }
 75 void Init()
 76 {
 77     for(int i=0;i < maxn;++i)
 78         G[i].clear(),rG[i].clear();
 79 }
 80 int main()
 81 {
 82 
 83     while(~scanf("%d",&n))
 84     {
 85         Init();
 86         for(int i=1;i <= n;++i)
 87         {
 88             int h,m;
 89             char ch;
 90             scanf("%d%c%d",&h,&ch,&m);
 91             s[i]=h*60+m;//将开始时间转化为分钟
 92             scanf("%d%c%d",&h,&ch,&m);
 93             t[i]=h*60+m;//将结束时间转化为分钟
 94             scanf("%d",d+i);
 95         }
 96         SAT();//根据2-SAT建图
 97         SCC();//强连通分量分解
 98         bool flag=false;
 99         for(int i=1;i <= n;++i)
100             if(scc[i] == scc[i+n])
101                 flag=true;
102         if(flag)
103             printf("NO\n");
104         else
105         {
106             printf("YES\n");
107             for(int i=1;i <= n;++i)
108             {
109                 if(scc[i] > scc[i+n])
110                     printf("%02d:%02d %02d:%02d\n",s[i]/60,s[i]%60,(s[i]+d[i])/60,(s[i]+d[i])%60);
111                 else
112                     printf("%02d:%02d %02d:%02d\n",(t[i]-d[i])/60,(t[i]-d[i])%60,t[i]/60,t[i]%60);
113             }
114         }
115     }
116     return 0;
117 }
View Code
posted @ 2018-10-10 21:49  HHHyacinth  阅读(245)  评论(0编辑  收藏  举报