09-07 HDU_Steps5.1 並查集 HDU1829 HDU1325 HDU1598 HDU3461 HDU3635 HDU2473 HDU3172 HDU3038

來源:互聯網
上載者:User

HDU STEP 5.1 並查集


5.1.1 HDU1829 A Bug's Life

給出異性對a,b 判斷是否有衝突,即給出a,b異性,a,c異性,又給出b,c異性,顯然是衝突了(同性戀~)

用opp[x]數組表示與x對立的bug,(其實就是判斷一個二分圖)

輸入a,b後顯然分四種情況

1,opp[a]==-1,opp[b]==-1,a和b都沒有出現過,分別加到各自的對立集合 opp[a]=b opp[b]=a

2,opp[a]!=-1,opp[b]==-1,b沒有出現過,將其合并到a的對立集合裡,opp[b]=a,merge(b,opp[a])

3.opp[a]==1,opp[b]!=-1同上處理

4.opp[a]!=-1,opp[b]!=-1,a和b都出現過,分別合并到各自的對立集合,merge(opp[a],b),merge(opp[b],a)

#include <cstdio>#include <cstdlib> using namespace std;const int MAXN=2010;//opp儲存與a異性的bug int cas,n,m,a,b,yes,p[MAXN],opp[MAXN]; char c;void init(){for(int i=1;i<=n;i++)p[i]=i,opp[i]=-1; yes=1;}int find(int x){if(x!=p[x])p[x]=find(p[x]);return p[x];}void merge(int x,int y){p[find(x)]=find(y); }inline void scan(int &x){while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';}int main(){scan(cas);for(int i=1;i<=cas;i++){scan(n);scan(m); init();while(m--){scan(a);scan(b);if(yes){//都沒有出現過 if(opp[a]==-1&&opp[b]==-1){opp[a]=b,opp[b]=a;//出現過其中一個,將另一個與它的異性集合合并 }else if(opp[a]==-1){opp[a]=b;merge(a,opp[b]);}else if(opp[b]==-1){opp[b]=a;merge(b,opp[a]);//都出現過,先尋找是否在一個集合中,若不在,和對方的異性集合合并 }else{if(find(a)==find(b))yes=0;else{merge(a,opp[b]);merge(b,opp[a]);}}}}printf("Scenario #%d:\n",i);printf(yes?"No suspicious bugs found!\n":"Suspicious bugs found!\n");printf("\n");}return 0;} 

5.1.2 HDU1325 Is It A Tree?

這題寫了我很久..只能說我對樹的理解還不夠深刻..

三個條件,不能有環,不能是森林,每個點入度最大為1(一直沒注意這種情況!!)

前兩個用並查集都可以判斷,入度用一個數組在讀取的時候儲存

#include <cstdio>#include <string.h> using namespace std;const int MAXN=100005;int par[MAXN],yes,hash[MAXN],in[MAXN];void init(){yes=1;memset(hash,0,sizeof hash);memset(in,0,sizeof in); for(int i=1;i<MAXN;i++)par[i]=i;} int find(int x){if(x!=par[x])par[x]=find(par[x]);return par[x];}void merge(int x,int y){par[find(x)]=find(y);}int main(){int a,b,cas=1,ta;init();while(scanf("%d%d",&a,&b)){if(a<0&&b<0)break;if(a==0&&b==0){if(yes){for(int i=1;i<MAXN;i++){if(hash[i]){ta=find(i);break;}}for(int i=1;i<MAXN;i++){//不能有點的入度為2 if(in[i]>1){yes=0;break;}//不能是森林 if(hash[i]&&ta!=find(i)){yes=0;break;}}}if(yes)printf("Case %d is a tree.\n",cas++);else printf("Case %d is not a tree.\n",cas++);init();}else{//不能產生環 if(find(a)==find(b))yes=0;if(!yes)continue;hash[a]=hash[b]=1;in[b]++; merge(a,b);}}}


5.1.3 HDU1598 find the most comfortable road

找一條路使路上的最大值和最小值之差最小

並查集可以判連通,但是沒有想到這題也可以用並查集做..

將邊的權值從小到大排序,然後開始枚舉,依次添加比它大的邊,知道起點和終點連通為止(find(u)==find(v)),取最小值作為結果

#include <cstdio>#include <cmath>#include <algorithm>using namespace std;struct sars{int u,v,w;bool operator <(const sars& s)const{return w<s.w;}}s[1001];int n,m,pa[201];void init(){for(int i=0;i<201;i++)pa[i]=i;}int find(int x){return x==pa[x]?x:pa[x]=find(pa[x]);}void merge(int x,int y){x=find(x),y=find(y);if(x!=y)pa[x]=y;}int solve(int st,int en){//判斷是否連通 init();for(int i=0;i<m;i++){merge(s[i].u,s[i].v);} if(find(st)!=find(en))return -1;//以每條邊為起始邊,依次添加權值比它大的邊,直到連通,連通邊-起始邊=差值 int res=1e9;for(int i=0;i<m;i++){init();for(int j=i;j<m;j++){if(s[j].w-s[i].w>=res)continue;merge(s[j].u,s[j].v);if(find(st)==find(en))res=min(res,s[j].w-s[i].w);}} return res;}char c;inline void scan(int &x){while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';}int main(){while(scanf("%d%d",&n,&m)!=EOF){int a,b,c;for(int i=0;i<m;i++){scan(s[i].u);scan(s[i].v);scan(s[i].w);}//按權值排序 sort(s,s+m);scan(a);for(int i=0;i<a;i++){scan(b);scan(c);printf("%d\n",solve(b,c));}}return 0;} 

5.1.4 HDU3461 Code Lock

一道好題,不過思路比較難想

如果沒有區間存在,答案是26^n,每增加一個區間,n-1(這裡試一下就知道了,證明也很容易,因為這個區間可以變成26種狀態~).但是要注意的是,比如已經有(1,10)和(1,3)在了,此時再增加(4,10)就沒有作用了..

使用並查集對於[l,r]我們將l,r+1兩個點並起,如果新線段的兩個點是同一個集合,就不用減了

#include <cstdio>using namespace std;const int MAXN=10000010;const int PMOD=1000000007;int n,m,a,b,ans;int p[MAXN];void init(){for(int i=1;i<=n+2;i++)p[i]=i;}int find(int x){return x==p[x]?x:p[x]=find(p[x]);}int merge(int x,int y){x=find(x),y=find(y);if(x==y)return 0;p[x]=y;return 1;}long long mi(int x){//二分求求26^x if(x==0)return 1;long long a=mi(x/2);a=a*a%PMOD;if(x%2==1)a=a*26%PMOD;return a;}int main(){while(scanf("%d%d",&n,&m)!=EOF){init();ans=0;while(m--){/*每加一個線段就會x1不過注意線段劃分時會出現重複比如 [1,10],[1,3]這時候再來一個[4,10]就不用減了,因為前兩個線段已經划出這個了使用並查集對於[l,r]我們將l,r+1兩個點並起如果新線段的兩個點是同一個集合,就不用減了*/scanf("%d%d",&a,&b);ans+=merge(a,b+1);}printf("%I64d\n",mi(n-ans));}}

5.1.5 HDU3635 Dragon Balls

很裸的並查集,用負節點標記集合元素個數,ts[x]代表x運送次數,在壓縮路徑很修改

#include <cstdio>#include <cstdlib>using namespace std;const int MAXN=11000;int cas,n,a,b,q,p[MAXN],ts[MAXN];char c[3],cc;void init(){for(int i=1;i<=n;i++){p[i]=-1;ts[i]=0;}}int find(int x){if(p[x]<0)return x;int t=p[x];p[x]=find(p[x]);ts[x]+=ts[t]; return p[x];}void merge(int x,int y){x=find(x),y=find(y);if(x!=y)p[y]+=p[x],p[x]=y,ts[x]=1;}inline void scan(int &x){while(cc=getchar(),cc<'0'||cc>'9');x=cc-'0';while(cc=getchar(),cc>='0'&&cc<='9')x=x*10+cc-'0';}int main(){scan(cas);for(int ca=1;ca<=cas;ca++){ printf("Case %d:\n",ca);scan(n);scan(q);init();while(q--){scanf("%s",c);if(c[0]=='T'){scan(a);scan(b); merge(a,b);}else{scan(a);int tt=find(a); printf("%d %d %d\n",tt,-p[tt],ts[a]);}}} //system("pause");return 0; } 

5.1.6 HDU2473 Junk-Mail Filter

好題啊..虛擬根節點,這樣在刪除時就不會影響到其它的節點了.

#include <cstdio>#include <string.h>using namespace std;int n,m,a,b,cnt,p[2000000],s[1000041],ans;char c;inline void scan(int &x){while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';}void init(){cnt=n*2,ans=0;for(int i=0;i<n;i++)p[i]=i+n;for(int i=n;i<n*2+m;i++)p[i]=i;memset(s,0,sizeof s);}int find(int x){return x==p[x]?x:p[x]=find(p[x]);}void merge(int x,int y){p[find(x)]=find(y);}//固定根節點,n+1~n+n作為根節點,而1~n作為虛擬根節點(指向n+1~n+n),之後在增添n+n~n+n+m作為備用節點//刪除時直接修改1~n指向的節點到n+n後的節點int main(){int cas=1;while(scanf("%d%d",&n,&m),n||m){init();for(int mm=0;mm<m;mm++){scanf(" %c",&c);if(c=='M'){scan(a);scan(b);merge(a,b);}else{scan(a);p[a]=cnt++;}}for(int i=0;i<n;i++){if(s[find(i)]==0){ans++,s[find(i)]=1;}}printf("Case #%d: %d\n",cas++,ans);}return 0;}


5.1.7 HDU3172 Virtual Friends

裸並查集,給字串寫個雜湊函數就可以了


5.1.8 HDU3038 How Many Answers Are Wrong

壓根沒想到怎麼用並查集做..還是看了大牛的解題報告

和上面那一題Code Lock有點像,[1,10]和[1,4]確定了,[5,10]也就確定了

所以將每次端點合并,sum[x]表示tot[x]-tot[root](即第x+1個數到第root個數的和) tot[x]表示前x個數的和


#include <cstdio>using namespace std;const int MAXN=200010;int n,m,ai,bi,si,wa;int p[MAXN],sum[MAXN];void init(){for(int i=0;i<=n;i++)p[i]=i,sum[i]=0;}int find(int x){if(x!=p[x]){int t=p[x];p[x]=find(p[x]);sum[x]+=sum[t]; }return p[x];}bool merge(int x,int y,int c){int px=find(x),py=find(y);if(px==py)return sum[x]-sum[y]==c;//sum[x]表示tot[r]-tot[x],r=root; p[px]=py;sum[px]=sum[y]-sum[x]+c;return true;}int main(){while(~scanf("%d%d",&n,&m)){wa=0;init();while(m--){scanf("%d%d%d",&ai,&bi,&si);if(!merge(ai-1,bi,si))wa++;}printf("%d\n",wa);}return 0;} 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.