POJ 1639 度限制最小生成树Prim

来源:互联网 发布:js form表单验证 编辑:程序博客网 时间:2024/04/29 23:37

题意:给出n条无向带权边,求所有点的最小生成树,其中“Park”的度数不超过最后输入的k,输入保证有解。

思路:思路其实很好理解,分为几个步骤:

1.当然将“Park”作为根节点,一开始先删掉它,则原图会分为m个连通分量,分别记录它们的最小生成树,并记录每个分量与根的最小边,于是我们得到了根度数为m时的最小生成树。若k < m,则无解。

2.然后考虑将生成树上的一些边替换成与根相邻的边,若根与节点v有边连接且这条边不在生成树上,则假设连接这条边,那么我们必须删去一条原生成树的边,且这条边一定是原生成树中v到根节点的链上最长的边(我的做法是每次开始前DFS维护所有节点的这条对应边),设其长为len,则生成树大小可减少 len - w[root][v]。每次找到一条最优的边,直到根度数达到k或生成树已经最小。

代码:

#include <cstdio>#include <iostream>#include <cstring>#include <map>#define For(i,j,k) for(int i = j;i <= (k);i ++)#define Set(i,j) memset(i, j, sizeof(i))using namespace std;const int N = 25;int G[N][N], f[N][N], n, m, k;map<string, int> M;int dis[N], be[N], pre[N], cnt;int Prim(int h){Set(dis, 0x3f);dis[h] = 0, ++cnt;int ret = 0, Mindis = 1e9, Mk;For(i,1,n-1){int k = 1;For(j,2,n) if(!be[j] && dis[j] < dis[k]) k = j;if(k == 1)break;be[k] = cnt, ret += dis[k];if(G[k][1] >= 0 && Mindis > G[k][1])Mindis = G[k][1], Mk = k;For(j,2,n)if(!be[j] && G[k][j] >= 0 && dis[j] > G[k][j])dis[j] = G[k][j], pre[j] = k;if(pre[k]) f[k][pre[k]] = f[pre[k]][k] = 0;}f[Mk][1] = f[1][Mk] = 0;return ret + Mindis;}int Mx[N], My[N], Mv[N];void update(int h, int fa){For(i,2,n) if(!f[h][i] && G[h][i] >= 0 && i != fa){Mx[i] = Mx[h], My[i] = My[h], Mv[i] = Mv[h];if(G[h][i] > Mv[h]) Mx[i] = h, My[i] = i, Mv[i] = G[h][i];update(i, h);}}void solve(){int ret = 0;For(i,2,n) if(!be[i])ret += Prim(i);int c = cnt;if(c > k) return;while(c < k){++c;update(1, 0);int Max = 0, p;For(i,2,n)if(G[1][i] >= 0 && f[1][i] && Mv[i] - G[1][i] > Max) Max = Mv[i] - G[1][i], p = i;if(!Max)break;ret -= Max;f[Mx[p]][My[p]] = f[My[p]][Mx[p]] = 1;f[1][p] = f[p][1] = 0;}printf("Total miles driven: %d\n", ret);}int main(){Set(G, -1);M["Park"] = 1;scanf("%d", &m);For(i,1,m){char x[15], y[15];int w;scanf("%s%s%d", x, y, &w);if(!M.count(x))M[x] = M.size();if(!M.count(y))M[y] = M.size();G[M[x]][M[y]] = G[M[y]][M[x]] = w;f[M[x]][M[y]] = f[M[y]][M[x]] = 1;}n = M.size();scanf("%d", &k);solve();return 0;}


4 0
原创粉丝点击