您的位置:首页 > 其它

POJ 1639 Picnic Planning【度限制最小生成树】

2013-07-30 20:36 351 查看


POJ 1639 Picnic Planning【度限制最小生成树】

分类: 【图论专辑】2010-08-02
03:47 706人阅读 评论(3) 收藏 举报

treestringinput算法

/* 第一道度限制生成树,写的比较崩溃@@

* 度限制生成树是指对生成树中某些点限制度的生成树,如果是要求权值最小的话

* 便是度限制最小生成树。一般题目也就是对某一个点进行度限制,因为所有点进行

* 度限制是NP问题。

* 解法:1)先去掉有度限制的点和与它相邻的边,对求剩下的点求最小生成树。由于剩下的点可能不连同,所以

* 要对每一个连通分量求最小生成树。

* 2)然后对每一个连通分量,从中找出一条距原点(有度限制的点)最短的一条边加进去。如果有M个连通分量,

* 那么现在求得的便是一个M度最小生成树。

* 3)设度限制为K > M,那么再进行 K-M 次循环,每次进行如下操作:对每一个不在生成树中的点,连一条到原点的

* 边,那么现在一定构成了一个环。然后找出这个环中除了新连的边外最长的边,和新添加的边作差。

* 如果结果是负值,找到最小的这个值,更新,如此进行K-M 次操作。

* 由于该题求的是不超过K度的生成树,所以如果无法更新便可直接跳出循环

* PS:以上便是度限制生成树的基本算法,个人感觉其中关键部分一个是对每一个连通分量求最小生成树,另一个便是添边然后找环

* 操作,网上的大部分解法都很复杂,代码动辄200+ 今天请教了Ych,他教了我一个好方法,可以这样:由于去掉原点后图可能

* 不连通,而对每一个连通分量求最小生成树又很烦,所以可以初始化时将所有边初始化为无穷大,然后对去掉原点的图只进行一次

* 求最小生成树的操作。这样如果图不连通的话那么会自动加一条无穷大的边,但是没关系。接下来在从生成树中找到一个距原点

* 最近的点加到生成树中,此时是一棵1度生成树(不是最小生成树),然后进行K-1次上述的更新。其中找环可以用DFS,也可以用

* BFS。我选择了BFS,因为更直观。

* 最后有一点一定要注意就是对某些变量赋值时的无穷大一定要比边初始化的那个无穷大还要大,否则会出错。今天因为这个错误调试

* 了近两个小时。。。崩溃。。。

*/

[cpp] view
plaincopy

#include <cstdio>

#include <iostream>

#include <cstring>

#include <algorithm>

#include <map>

#include <queue>

#include <string>

#define min(a,b) ((a)<(b)?(a):(b))

#define INF (1<<30)

#define MAXN 25

using namespace std;

int cost[MAXN][MAXN],pre[MAXN],dis[MAXN];

bool vis[MAXN],tree[MAXN][MAXN];

int M,N,K,ans;

inline bool Input(){

char s[20],e[20];

int w,cont = 1;

map<string,int>f;

f.clear();

f["Park"] = 0;

memset(cost,0x3f,sizeof(cost));

while(M--){

scanf("%s %s %d",s,e,&w);

if(f.find((string)s) == f.end())

f[(string)s] = cont++;

if(f.find((string)e) == f.end())

f[(string)e] = cont++;

int u = f[(string)s],v = f[(string)e];

cost[u][v] = cost[v][u] = w;

}

N = cont;

scanf("%d",&K);

return true;

}

inline int Find_max(int s,int e,int &st,int &ed){ // BFS找到环上边权最大的边

int que[MAXN+10];

memset(vis,false,sizeof(vis));

vis[s] = true;

int head = 0,tail = 0;

pre[s] = -1;

que[tail++] = s;

while(tail > head){

int tep = que[head++];

for(int i = 1;i < N; ++i){

if(!vis[i] && tree[tep][i]){

vis[i] = true;

pre[i] = tep;

que[tail++] = i;

}

}

}

int re = -1;

while(s != e){

if(re < cost[e][pre[e]]){

re = cost[e][pre[e]];

st = e;

ed = pre[e];

}

e = pre[e];

}

return re;

}

inline int prim(){

memset(vis,false,sizeof(vis));

memset(tree,false,sizeof(tree));

for(int i = 1;i < N; ++i){

pre[i] = 1;

dis[i] = cost[1][i];

}

vis[1] = true;

int sum = 0;

for(int i = 1;i < N-1; ++i){

int md = INF,idex = -1;

for(int j = 1;j < N ; ++j){

if(!vis[j] && dis[j] < md){

md = dis[j];

idex = j;

}

}

if(idex == -1) break;

vis[idex] = true;

tree[idex][pre[idex]] = tree[pre[idex]][idex] = true;

sum += md;

for(int j = 1;j < N; ++j)

if(!vis[j] && dis[j] > cost[idex][j]){

dis[j] = cost[idex][j];

pre[j] = idex;

}

}

return sum;

}

inline void solve(){

ans = prim(); //先对剩余点求最小生成树

int m = INF,idex = -1;

for(int i = 1;i < N; ++i){ // 然后从生成树中找一个距度限制点最近的点加进来

if(cost[i][0] < m){

m = cost[i][0];

idex = i;

}

}

ans += m;

tree[idex][0] = tree[0][idex] = true;

for(int i = 1;i < K; ++i){ //然后进行K-1次循环每次进行树的更新

int st,ed,s,e;

idex = -1; m = 0;

for(int j = 1,k;j < N; ++j){

if(!tree[j][0]){

k = Find_max(0,j,st,ed);

if(m > cost[j][0] - k){

m = cost[j][0] - k;

idex = j;

s = st;

e = ed;

}

}

}

if(idex == -1) break;

ans += m;

tree[idex][0] = tree[0][idex] = true;

tree[s][e] = tree[e][s] = false;

}

printf("Total miles driven: %d/n",ans);

}

int main()

{

while(scanf("%d",&M) != EOF){

Input();

solve();

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: