2016年ACM/ICPC大連賽區 A題(二分圖判定/Two-Sat)
阿新 • • 發佈:2018-12-13
題意:
給你四個數,n個人(每個人不是好人就是壞人),m個二元組,再給你x個整數,y個整數(n<=1e3,m<=1e5,x+y<=n)
m個二元組(a,b)表示 a,b不能同時為好人或壞人。
x個整數,每個整數c表示c一定是好人。
y個整數,每個整數d表示d一定是壞人。
求是否存在一組合法的解使得滿足上面所有條件。有的話輸出YES,否則輸出NO。(注意大小寫)
思路:典型的二分圖判定。對於x和y直接染成不同的顏色,有矛盾的話直接標記為NO。
然後套個二分圖判定模板就行了。用2-sat也可以做。
二分圖判定做法程式碼:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f3f3f3f3fLL using namespace std; const int maxn=100010; int fcount(int x) //求數x二進位制下所含1的個數 { int s=0; while(x){ s++; x&=(x-1); } return s; } vector<int> v[100005], a, b; int ok = 1, lor[100005],lorr[100005],vis[100005]; int C[maxn],tot; void Sech(int x); int main(void) { int i, n, m, c, d,nn,mm; while(scanf("%d%d%d%d",&n,&m,&nn,&mm)!=EOF){ memset(lor,0,sizeof(lor)); memset(lorr,0,sizeof(lorr)); for(i=0;i<=n;i++) v[i].clear(),vis[i]=0; ok=1;tot=0;a.clear();b.clear(); for(i=1;i<=m;i++) { scanf("%d%d", &c, &d); if(!vis[c]){vis[c]=1;C[++tot]=c;} if(!vis[d]){vis[d]=1;C[++tot]=d;} v[c].push_back(d); v[d].push_back(c); } int x; for(int i=0;i<nn;i++) { scanf("%d",&x); vis[x]=1; lor[x]=1; lorr[x]=-1; } for(int i=0;i<mm;i++) { scanf("%d",&x); if(lor[x]==1) {ok=0;} vis[x]=1; lor[x]=-1; lorr[x]=1; } for(int i=1;i<=n;i++) if(!vis[i]) {ok=0;break;} for(int i=1;i<=n;i++) if(lor[i])for(int j=0;j<v[i].size();j++) { if(lor[v[i][j]]==lor[i]) {ok=0;break;} } if(!ok) {puts("NO");continue;} for(int ii=1;ii<=tot;ii++) { i=C[ii]; if(lor[i]==0) { lor[i] = 1; a.push_back(i); Sech(i); } } if(ok==0) { // cout<<" *"<<endl; ok=1;a.clear();b.clear(); for(int i=0;i<=n;i++) lor[i]=lorr[i]; for(int ii=1;ii<=tot;ii++) { int i=C[ii]; if(lor[i]==0) { lor[i] = 1; a.push_back(i); Sech(i); } } if(ok==0) puts("NO"); else puts("YES"); } else printf("YES\n"); } return 0; } void Sech(int x) { int i, t; if(ok==0) return; for(i=0;i<v[x].size();i++) { t = v[x][i]; if(lor[t]==lor[x]) ok = 0; else { if(lor[x]==1 && lor[t]==0) { b.push_back(t), lor[t] = -1; Sech(t); } else if(lor[x]==-1 && lor[t]==0) { a.push_back(t), lor[t] = 1; Sech(t); } } } }
#include<bits/stdc++.h> using namespace std; const int maxn=10000+10; bool vis[maxn*2]; struct TwoSAT { int n;//原始圖的節點數(未翻倍) vector<int> G[maxn*2];//G[i]==j表示如果mark[i]=true,那麼mark[j]也要=true bool mark[maxn*2];//標記 int S[maxn*2],c;//S和c用來記錄一次dfs遍歷的所有節點編號 void init(int n) { this->n=n; for(int i=0;i<2*n;i++) G[i].clear(); memset(mark,0,sizeof(mark)); } //加入(x,xval)或(y,yval)條件 //xval=0表示假,yval=1表示真 void add_clause(int x,int xval,int y,int yval) { x=x*2+xval; y=y*2+yval; G[x^1].push_back(y); G[y^1].push_back(x); } //從x執行dfs遍歷,途徑的所有點都標記 //如果不能標記,那麼返回false bool dfs(int x) { if(mark[x^1]) return false;//這兩句的位置不能調換 if(mark[x]) return true; mark[x]=true; S[c++]=x; for(int i=0;i<G[x].size();i++) if(!dfs(G[x][i])) return false; return true; } //判斷當前2-SAT問題是否有解 bool solve() { for(int i=0;i<2*n;i+=2) if(!mark[i] && !mark[i+1]) { //printf("fsd sfdg %d\n",i); c=0; if(!dfs(i)) { while(c>0) mark[S[--c]]=false; if(!dfs(i+1)) return false; } } return true; } }fun; int main() { int i,j,x,y,aa,bb,m,n; while(~scanf("%d%d%d%d",&n,&m,&x,&y)) { fun.init(n); memset(vis,0,sizeof(vis)); for(i=0;i<m;i++) { scanf("%d%d",&aa,&bb); aa--; bb--; fun.add_clause(aa,1,bb,1); fun.add_clause(aa,0,bb,0); vis[aa]=1; vis[bb]=1; } for(i=0;i<x;i++) { scanf("%d",&aa); aa--; vis[aa]=1; fun.add_clause(aa,1,aa,1); } for(i=0;i<y;i++) { scanf("%d",&aa); aa--; vis[aa]=1; fun.add_clause(aa,0,aa,0); } bool flag; flag=-1; for(i=0;i<n;i++) { if(vis[i]==0) { flag=0; break; } } if(flag==0) { printf("NO\n"); continue; } flag=fun.solve(); if(flag) printf("YES\n"); else printf("NO\n"); } }