ACM入门之【图论习题】
生活随笔
收集整理的這篇文章主要介紹了
ACM入门之【图论习题】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
目錄
- P5318 【深基18.例3】查找文獻【★ 圖的遍歷】
- P3916 圖的遍歷【★★ 求每一個點可以到達的最大的點 反向建圖】
- P1113 雜務【★ ★ 拓撲排序 求完成所有雜務所需的最短時間】
- P4017 最大食物鏈計數【★ ★ 拓撲排序 求鏈數】
- P1807 最長路【★ ★ 最長路】
- P2853 [USACO06DEC]Cow Picnic S【★ 圖的遍歷】
- P3371 【模板】單源最短路徑(弱化版)【★ 最短路】
- P1629 郵遞員送信【★★ 一去一回的最短路 反向建圖】
- P4779 【模板】單源最短路徑(標準版)【★ 最短路】
- P1144 最短路計數【★★ 統計最短路的數量】
- P3366 【模板】最小生成樹【★ 最小生成樹】
- P2872 [USACO07DEC]Building Roads S【★★ 最小生成樹】
- P1991 無線通訊網【★★ 最小生成樹】
- P1396 營救【★★ 最小生成樹 讓一個點到另一個點的最大值最小】
- P2121 拆地毯【★★ 最小生成樹的變種】
- P1194 買禮物【★★ 最小生成樹】
- P1195 口袋的天空【★★ 最小生成樹】
P5318 【深基18.例3】查找文獻【★ 圖的遍歷】
考察的就是基礎的圖的遍歷。
P3916 圖的遍歷【★★ 求每一個點可以到達的最大的點 反向建圖】
求一個點到最大的點,等價于最大的點到它可以到達的點。
故我們可以反向建圖。從大到小枚舉所有的點,如果該點遍歷過了,說明有一個更大的點之前來過(因為我們是先枚舉大結點),故遍歷過的可以不用遍歷。那么總的時間復雜度就是線性的O(m),完全可以過。
P1113 雜務【★ ★ 拓撲排序 求完成所有雜務所需的最短時間】
比較容易的想到的是拓撲排序。這好像是拓撲排序的一個非常經典的模型。
但問題是如何求,不難想到的是答案就是最晚的點的結束時間。
例:A完成需要先完成B,C。 故A結束的時間=A需要花費的時間+max(B,C)花費的時間
#include<bits/stdc++.h> using namespace std; const int N=1e5*6+10; typedef long long int LL; int h[N],e[N],ne[N],idx; LL w[N],st[N],cnt[N],f[N],n,ans; void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void topsort() {queue<int>q;for(int i=1;i<=n;i++) if(!cnt[i]) q.push(i),st[i]=1;while(q.size()){int u=q.front(); q.pop();for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];f[j]=max(f[j],w[u]);//求需要完成的先前任務的最大時間if(--cnt[j]==0) {if(!st[j]) q.push(j),st[j]=1,w[j]+=f[j];};}}for(int i=1;i<=n;i++) ans=max(ans,w[i]);//枚舉所有的點,存最晚結束的時間cout<<ans; } int main(void) {memset(h,-1,sizeof h);cin>>n;for(int i=1;i<=n;i++){int x,id;cin>>id>>x;w[id]=x;while(cin>>x,x) add(x,id),cnt[id]++;}topsort();return 0; }P4017 最大食物鏈計數【★ ★ 拓撲排序 求鏈數】
既然 食物鏈中的生物 可以看成 節點,那么 最佳生產者 的入度一定為 0, 而 最佳消費者 的出度也為 0。
例子:B->A, C->A, 故以A為終點的條數等于 到B點的條數+到C點的條數。這是一個遞推累加的過程。
P1807 最長路【★ ★ 最長路】
傳統的是最短路,那么如何求最長路呢?只需將邊權乘以(-1) 就變成了求最短路。
最后結果記得再乘以(-1)回來就是結果。
P2853 [USACO06DEC]Cow Picnic S【★ 圖的遍歷】
對于每一個奶牛所在的農場遍歷一遍圖。
吐過最后某個農場的計數是k則累加即可。
P3371 【模板】單源最短路徑(弱化版)【★ 最短路】
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> pii; const int N=1e4+10; const int M=1e5*5+10; int h[N],e[M],ne[M],w[M],idx; int st[N],dist[N],n,m,s; void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; } void Dijkstra(int s) {for(int i=1;i<=n;i++) dist[i]=(1ll<<31)-1;dist[s]=0;priority_queue<pii,vector<pii>,greater<pii> >q; q.push({0,s});while(q.size()){auto temp=q.top(); q.pop();int u=temp.second;if(st[u]) continue;st[u]=1;for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[u]+w[i]){dist[j]=dist[u]+w[i];q.push({dist[j],j});}}} } int main(void) {memset(h,-1,sizeof h);scanf("%d%d%d",&n,&m,&s);for(int i=1;i<=m;i++){int a,b,c; scanf("%d%d%d",&a,&b,&c);add(a,b,c);}Dijkstra(s);for(int i=1;i<=n;i++) cout<<dist[i]<<" "; }P1629 郵遞員送信【★★ 一去一回的最短路 反向建圖】
問題在于如何求每一個點回去的時候的最短路。
考慮到回去的最短路,故反向建圖這樣就可以正著跑了。
P4779 【模板】單源最短路徑(標準版)【★ 最短路】
#include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<map> using namespace std; const int N=1e5+10; const int M=1e5*2+10; typedef pair<int,int> PII; int h[N],e[M],ne[M],w[M],idx; int dist[N]; bool st[N]; int n,m,s; void add(int a,int b,int c) {e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; } void spfa(int s) {memset(dist,0x3f,sizeof dist);dist[s]=0;st[s]=true;queue<int> q; q.push(s);while(q.size()){int t=q.front(); q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];if(!st[j]) q.push(j),st[j]=true;}}} } int main(void) {memset(h,-1,sizeof h);scanf("%d%d%d",&n,&m,&s);while(m--){int a,b,c; scanf("%d%d%d",&a,&b,&c);add(a,b,c);}spfa(s);for(int i=1;i<=n;i++) cout<<dist[i]<<" ";cout<<endl;return 0; }P1144 最短路計數【★★ 統計最短路的數量】
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; const int M=1e6*4+10; const int mod=100003; int h[N],e[M],ne[M],idx; int cnt[N],dist[N],n,m; void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;} void bfs() {memset(dist,0x3f,sizeof dist);dist[1]=0,cnt[1]=1;queue<int>q; q.push(1);while(q.size()){int u=q.front(); q.pop();for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(dist[j]>dist[u]+1){dist[j]=dist[u]+1;cnt[j]=cnt[u];q.push(j);}else if(dist[j]==dist[u]+1) {cnt[j]=(cnt[j]+cnt[u])%mod;}}} } int main(void) {memset(h,-1,sizeof h);cin>>n>>m;for(int i=0;i<m;i++){int a,b; cin>>a>>b;add(a,b),add(b,a);}bfs();for(int i=1;i<=n;i++) cout<<cnt[i]<<endl;return 0; }P3366 【模板】最小生成樹【★ 最小生成樹】
#include<bits/stdc++.h> using namespace std; const int N=1e4+10; int p[N],n,m; struct node{int a,b,c;}; bool cmp(node a,node b){return a.c<b.c;} vector<node>ve; int find(int x) {if(x!=p[x]) p[x]=find(p[x]);return p[x]; } int f() {int res=0,cnt=0;for(int i=1;i<=n;i++) p[i]=i;sort(ve.begin(),ve.end(),cmp);for(int i=0;i<ve.size();i++){int a=ve[i].a,b=ve[i].b,c=ve[i].c;if(find(a)!=find(b)){p[find(a)]=find(b);res+=c;cnt++;}}if(cnt==n-1) return res;else return -1; } int main(void) {cin>>n>>m;while(m--){int a,b,c; cin>>a>>b>>c;ve.push_back({a,b,c});}int ans=f();if(ans==-1) puts("orz");else cout<<ans;return 0; }P2872 [USACO07DEC]Building Roads S【★★ 最小生成樹】
#include<bits/stdc++.h> using namespace std; const int N=1e4+10; typedef pair<double,double> PII; struct node {int a,b;double c; }; bool cmp(node a,node b){return a.c<b.c;} int n,m,p[N]; vector<node>ve; vector<PII>a; int find(int x) {if(x!=p[x]) p[x]=find(p[x]);return p[x]; } double get(PII a,PII b) {double x=(a.first-b.first)*(a.first-b.first);double y=(a.second-b.second)*(a.second-b.second);return sqrt(x+y); } void solve() {double ans=0;sort(ve.begin(),ve.end(),cmp);for(int i=0;i<ve.size();i++)//最小生成樹{int a=ve[i].a,b=ve[i].b;double c=ve[i].c;if(find(a)==find(b)) continue;p[find(a)]=find(b);ans+=c;}printf("%.2lf",ans); } int main(void) {cin>>n>>m;for(int i=0;i<n;i++){double x,y; cin>>x>>y;a.push_back({x,y});}for(int i=1;i<=n;i++) p[i]=i;for(int i=0;i<m;i++){int a,b; cin>>a>>b;p[find(a)]=find(b);}for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)//存所有的邊{double c=get(a[i],a[j]);ve.push_back({i+1,j+1,c});}solve();return 0; }P1991 無線通訊網【★★ 最小生成樹】
也就是說衛星電話是無需距離的。
我們先構造一個最小生成樹,然后再減邊。讓其用衛星電話。
例如: 3給衛星電話 可以減2條邊
P1396 營救【★★ 最小生成樹 讓一個點到另一個點的最大值最小】
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; struct node{int a,b,c;}; bool cmp(node a,node b){return a.c<b.c;} int p[N],n,m,s,t; vector<node>ve; int find(int x) {if(x!=p[x]) p[x]=find(p[x]);return p[x]; } void solve() {for(int i=1;i<=n;i++) p[i]=i;sort(ve.begin(),ve.end(),cmp);for(int i=0;i<m;i++){int a=ve[i].a,b=ve[i].b,c=ve[i].c;p[find(a)]=find(b);if(find(s)==find(t)) //說明聯通了{cout<<c;return;}} } int main(void) {cin>>n>>m>>s>>t;for(int i=0;i<m;i++){int a,b,c; cin>>a>>b>>c;ve.push_back({a,b,c});}solve();return 0; }P2121 拆地毯【★★ 最小生成樹的變種】
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; int p[N],n,m,k; struct node{int a,b,c;}; bool cmp(node a,node b){return a.c>b.c;}//從大到小排 vector<node>ve; int find(int x) {if(x!=p[x]) p[x]=find(p[x]);return p[x]; } void solve() {for(int i=1;i<=n;i++) p[i]=i;sort(ve.begin(),ve.end(),cmp);int cnt=0,ans=0;for(int i=0;i<ve.size();i++){int a=ve[i].a,b=ve[i].b,c=ve[i].c;if(find(a)==find(b)) continue;ans+=c,cnt++;p[find(a)]=find(b);if(cnt>=k) break;}cout<<ans; } int main(void) {cin>>n>>m>>k;for(int i=0;i<m;i++){int a,b,c; cin>>a>>b>>c;ve.push_back({a,b,c});}solve();return 0; }P1194 買禮物【★★ 最小生成樹】
#include<bits/stdc++.h> using namespace std; const int N=1010; int price,n,g[N][N],p[N]; struct node{int a,b,c;}; vector<node>ve; bool cmp(node a,node b){return a.c<b.c;} int find(int x) {if(x!=p[x]) p[x]=find(p[x]);return p[x]; } void solve() {for(int i=1;i<=n;i++) p[i]=i;sort(ve.begin(),ve.end(),cmp);int cnt=1,w=price;for(int i=0;i<ve.size();i++){int a=ve[i].a,b=ve[i].b,c=ve[i].c;if(find(a)==find(b)) continue;if(c>=price) continue;//也就是說不如直接買w+=c,cnt++;p[find(a)]=find(b);}cout<<(n-cnt)*price+w;//(n-cnt) 就是還沒買的 } int main(void) {cin>>price>>n;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) cin>>g[i][j];for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)if(g[i][j]) ve.push_back({i,j,g[i][j]});solve();return 0; }P1195 口袋的天空【★★ 最小生成樹】
先構造最小生成樹,再減最大的邊讓其變成k個連通塊
總結
以上是生活随笔為你收集整理的ACM入门之【图论习题】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 112. 雷达设备【贪心】
- 下一篇: C. Woodcutters【贪心】