poj 2831(次小生成树)
題意:給你一幅圖,再給你Q個詢問,每個詢問為id cost,即如果將id這條邊的邊權改為cost的話,這條邊是否可能是最小生成樹中的一條邊
解題思路:將第i條邊(u,v)的權值修改的話,要判斷是否是最小生成樹中的一條邊,首先要把它加入進去,此時必定會引起原來的生成樹成環,所以必定要擦去一條邊,擦去的是哪一條邊,這就利用到了次小生成樹的原理了。
之前寫過一個次小生成樹的題,現在回過頭看,感覺又有點不對了。
這里再總結一次:
首先肯定是構造一顆最小生成樹,接下來就是枚舉不在生成樹里的邊,假定為(u,v),此時應該把它加入到生成樹中,但這樣肯定會形成u->v的環路,此時肯定要刪除u->v這條環路里的邊,刪哪一條呢?肯定是除了邊(u,v)外的最大邊。
現在是如何找到這條最大邊,可以采用dp的思想,即dp[i][j]表示在樹上i->j的最大值(注意,由于是樹,肯定i->j的路徑是唯一的)。我們在做Prim算法時,每次都是加入一個頂點S,我們還應該記錄下頂點S加入到生成樹里,它與誰相連,即我們在更新low[]數組時要記錄的。這樣,我們可以得到狀態方程:dp[i][j] = dp[j][i] = max(dp[j][pre[i]],low[i]),此時i為即將要加入的點,而j是已經在生成樹頂點集合里的點。
完成了一次Prim算法,同樣也可以將dp數組更新好了,那么回到最開始的問題,刪除的邊我們就可以直接用dp[u][v]。
#include<iostream> #include<cstdio> #include<cstring> using namespace std;const int maxn = 100005; const int inf = 0x3f3f3f3f; struct Edge {int u,v,c; }edge[maxn]; int n,m,q; int map[1005][1005],path[1005][1005]; int pre[1005],low[1005]; bool vis[1005],used[1005][1005];int Prim() {int k = 1,ans = 0;memset(path,0,sizeof(path));memset(vis,false,sizeof(vis));memset(used,false,sizeof(used));vis[k] = true;for(int i = 1; i <= n; i++){low[i] = map[k][i];pre[i] = k;}for(int i = 1; i < n; i++){int MIN = inf;for(int j = 1; j <= n; j++)if(vis[j] == false && low[j] < MIN){MIN = low[j];k = j;}ans += MIN;vis[k] = true;used[k][pre[k]] = used[pre[k]][k] = true;for(int j = 1; j <= n; j++){if(vis[j] == true && j != k)path[j][k] = path[k][j] = max(path[j][pre[k]],low[k]);if(vis[j] == false && low[j] > map[k][j]){low[j] = map[k][j];pre[j] = k;}}}return ans; }int main() {while(scanf("%d%d%d",&n,&m,&q)!=EOF){memset(map,inf,sizeof(map));for(int i = 1; i <= m; i++){scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c);map[edge[i].u][edge[i].v] = map[edge[i].v][edge[i].u] = min(map[edge[i].u][edge[i].v],edge[i].c);}int ans = Prim();while(q--){int id,cost;scanf("%d%d",&id,&cost);if(path[edge[id].u][edge[id].v] >= cost)printf("Yes\n");else printf("No\n");}}return 0; }
總結
以上是生活随笔為你收集整理的poj 2831(次小生成树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用自定义的 ClassLoader 加
- 下一篇: hdu 1558(线段相交+并查集)