POJ - 3683 Priest John's Busiest Day(2-SAT+路径打印)
題目鏈接:點擊查看
題目大意:現在有n對新人要結婚,每一場婚禮都要請牧師主持一個特殊的儀式,這個儀式必須在婚禮的前d分鐘或者最后d分鐘進行,現在問能否有一種安排,能讓牧師參加到每一場婚禮去主持儀式,輸出方案
題目分析:也是一個2-SAT模板題,只是路徑打印有點不好理解,我們可以將兩個賦值拆成前d分鐘主持儀式和后d分鐘主持儀式,這樣每個點都有兩種賦值情況了,并且我們可以預先儲存n場婚禮的時間,隨后n*n來枚舉每兩場婚禮,沖突的話就建立約束關系,無非只有下面四種情況:
這樣按照約束建好邊后跑一邊強連通縮點就能判斷是否有答案了,對于路徑打印可以用拓撲序反著跑,不過因為tarjan的本質是dfs,也就保證了其每個scc編號的特殊性質,因為每次回溯時會先取出有向圖“底部”的scc進行標記,所以tarjan算法得到的scc編號實際上就是縮點后“自底向上”的拓撲序了
然后還有一個不太理解的地方,就是對于一組i和i+n,會優先選擇拓撲序小的進行輸出,先記住吧
還有一點需要注意的地方,這里主持儀式的時間其實可以視為一個左閉右開或者左開右閉的區間,因為如果在某一個時間點牧師剛結束了一個婚禮的主持,則在當前分鐘可以再去給接下來的一個婚禮繼續主持(高強度工作。。),所以在判斷區間是否相交的時候需要特別注意一下大于號和大于等于號的區別,以及判斷區間相交也不用太復雜,無非就是判斷一下一場的開始點是否在另一場結束點之后,如果兩場婚禮的起止時間都滿足條件則說明不相交,否則就說明相交了
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e3+100;const int M=N*N;struct Node {int st,ed; }a[N];struct Egde {int to,next; }edge1[M];int head1[N],low[N],dfn[N],c[N],Stack[N],num,cnt,cnt2,cnt1,dcc,n,m,top;bool ins[N];vector<int>scc[N];void addedge1(int u,int v) {edge1[cnt1].to=v;edge1[cnt1].next=head1[u];head1[u]=cnt1++; }void tarjan(int u) {dfn[u]=low[u]=++num;Stack[++top]=u;ins[u]=true;for(int i=head1[u];i!=-1;i=edge1[i].next){int v=edge1[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(ins[v])low[u]=min(low[u],dfn[v]);}if(dfn[u]==low[u]){cnt++;int v;do{v=Stack[top--];ins[v]=false;c[v]=cnt;scc[cnt].push_back(v);}while(u!=v);} }void solve() {for(int i=1;i<=2*n;i++)//縮點 if(!dfn[i])tarjan(i); }void init() {for(int i=0;i<N;i++)scc[i].clear();top=cnt=cnt1=num=dcc=0;memset(head1,-1,sizeof(head1));memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(c,0,sizeof(c));memset(ins,false,sizeof(ins)); }bool include(int x,int y) {if(a[x].st<a[y].ed&&a[y].st<a[x].ed)return true;return false; }bool check() {for(int i=1;i<=n;i++)if(c[i]==c[i+n])return false;return true; }void print(Node& a) {printf("%02d:%02d %02d:%02d\n",a.st/60,a.st%60,a.ed/60,a.ed%60); }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){init();for(int i=1;i<=n;i++){int h,m;scanf("%d:%d",&h,&m);int time1=h*60+m;scanf("%d:%d",&h,&m);int time2=h*60+m;int d;scanf("%d",&d);a[i].st=time1;a[i].ed=time1+d;a[i+n].st=time2-d;a[i+n].ed=time2;}for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++){if(i==j)continue;if(include(i,j)){addedge1(i,j+n);addedge1(j,i+n);}if(include(i,j+n)){addedge1(i,j);addedge1(j+n,i+n);}if(include(i+n,j)){addedge1(i+n,j+n);addedge1(j,i);}if(include(i+n,j+n)){addedge1(i+n,j);addedge1(j+n,i);}}solve();if(check()){puts("YES");for(int i=1;i<=n;i++)if(c[i]<c[i+n])print(a[i]);elseprint(a[i+n]);}elseputs("NO");}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 3683 Priest John's Busiest Day(2-SAT+路径打印)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ - 3678 Katu Puzz
- 下一篇: POJ - 1041 John's tr