Let's go home(HDU-1824)
Problem Description
小時候,鄉愁是一枚小小的郵票,我在這頭,母親在那頭。
? ? ? ? ? ? ? ? ? ? ? ? —— 余光中
集訓是辛苦的,道路是坎坷的,休息還是必須的。經過一段時間的訓練,lcy決定讓大家回家放松一下,但是訓練還是得照常進行,lcy想出了如下回家規定,每一個隊(三人一隊)或者隊長留下或者其余兩名隊員同時留下;每一對隊員,如果隊員A留下,則隊員B必須回家休息下,或者B留下,A回家。由于今年集訓隊人數突破往年同期最高記錄,管理難度相當大,lcy也不知道自己的決定是否可行,所以這個難題就交給你了,呵呵,好處嘛~,免費**漂流一日。
Input
第一行有兩個整數,T和M,1<=T<=1000表示隊伍數,1<=M<=5000表示對數。
接下來有T行,每行三個整數,表示一個隊的隊員編號,第一個隊員就是該隊隊長。
然后有M行,每行兩個整數,表示一對隊員的編號。
每個隊員只屬于一個隊。隊員編號從0開始。
Output
可行輸出yes,否則輸出no,以EOF為結束。
Sample Input
1 2
0 1 2
0 1
1 2
2 4
0 1 2
3 4 5
0 3
0 4
1 3
1 4
Sample Output
yes
no
思路:每個人有兩種選擇,根據題意要滿足兩種條件:
1)隊長留 或 兩個隊員留
2)由 M 指出的一對隊員 a、b 的沖突條件
假設 a、b、c 三個組成一隊,a 是隊長,那么由條件 1 可知 隊長 a 與隊員 b、c 二者只能選一種,假設留為 1 走為 0,則:
對于任一人走:
- a 走,導致 b、c 留:<a,0,b,1>、<a,0,c,1>,添邊:(a+3*n,b)、(a+3*n,c)
- b 走,導致 a 留,c 走:<b,0,a,1>,添邊:(b+3*n,a)
- c 走,導致 a 留,b 走:<c,0,a,1>,添邊:(c+3*n,a)
對于 M 個條件:
- a 留,導致 b 走:<a,b+3*n>
- b 留,導致 a?走:<b,a+3*n>
根據以上關系,添加關系至 2-SAT 中判斷即可
Source Program
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> #include<vector> #include<set> #include<map> #include<bitset> #define EnewPosstr 1e-9 #define newPosI acos(-1.0) #define INF 0x3f3f3f3f #define LL long long const int MOD = 1E9+7; const int N = 1000000+5; const int dx[] = {-1,1,0,0,-1,-1,1,1}; const int dy[] = {0,0,-1,1,-1,1,-1,1}; using namespace std;struct Edge{int to,next; }edge[N*2]; int head[N],tot; int n,m; int dfn[N],low[N]; bool vis[N];//標記數組 int scc[N];//記錄結點i屬于哪個強連通分量 int block_cnt;//時間戳 int sig;//記錄強連通分量個數 stack<int> S; void init(){tot=0;sig=0;block_cnt=0;memset(head,-1,sizeof(head));memset(vis,0,sizeof(vis));memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(scc,0,sizeof(scc)); } void addEdge(int from,int to){edge[++tot].to=to;edge[tot].next=head[from];head[from]=tot; } void Tarjan(int x) {vis[x]=true;dfn[x]=low[x]=++block_cnt;//每找到一個新點,紀錄當前節點的時間戳S.push(x);//當前結點入棧for(int i=head[x]; i!=-1; i=edge[i].next) { //遍歷整個棧int y=edge[i].to;//當前結點的下一結點if(!dfn[y]) {Tarjan(y);low[x]=min(low[x],low[y]);}else if(vis[y])low[x]=min(low[x],dfn[y]);}if(dfn[x]==low[x]) { //滿足強連通分量要求sig++;//記錄強連通分量個數while(true) { //記錄元素屬于第幾個強連通分量int temp=S.top();S.pop();vis[temp]=false;scc[temp]=sig;if(temp==x)break;}} } bool twoSAT(){for(int i=1;i<=6*n;i++)//總點數if(!dfn[i])Tarjan(i);for(int i=1;i<=3*n;i++)if(scc[i]==scc[i+3*n])//遍歷是不是會有一個人有2種可能return false;return true; } int main() {while( scanf("%d%d",&n,&m)!=EOF&&(n+m)){init();for(int i=1;i<=n;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);a++,b++,c++;addEdge(a+3*n,b);//a不留b留addEdge(a+3*n,c);//a不留c留addEdge(b+3*n,a);//b不留a留addEdge(c+3*n,a);//c不留a留}while(m--) {int a,b;scanf("%d%d",&a,&b);a++,b++;addEdge(a,b+3*n);//a留b不留addEdge(b,a+3*n);//b留a不留}bool flag=twoSAT();if(!flag)printf("no\n");elseprintf("yes\n");}return 0; }?
總結
以上是生活随笔為你收集整理的Let's go home(HDU-1824)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 输出前k大的数(信息学奥赛一本通-T12
- 下一篇: 求10000以内n的阶乘(信息学奥赛一本