poj 3683(2-SAT+SCC)
Posted violet-acmer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 3683(2-SAT+SCC)相关的知识,希望对你有一定的参考价值。
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 "); 104 else 105 { 106 printf("YES "); 107 for(int i=1;i <= n;++i) 108 { 109 if(scc[i] > scc[i+n]) 110 printf("%02d:%02d %02d:%02d ",s[i]/60,s[i]%60,(s[i]+d[i])/60,(s[i]+d[i])%60); 111 else 112 printf("%02d:%02d %02d:%02d ",(t[i]-d[i])/60,(t[i]-d[i])%60,t[i]/60,t[i]%60); 113 } 114 } 115 } 116 return 0; 117 }
以上是关于poj 3683(2-SAT+SCC)的主要内容,如果未能解决你的问题,请参考以下文章
poj 3683 Priest John's Busiest Day - 2-sat
POJ 3683 Priest John's Busiest Day(2-SAT)
POJ 3683.Priest John's Busiest Day 2-SAT