您的位置:首页 > 其它

zoj - 1406 - Jungle Roads

2012-12-11 00:39 288 查看
题意:有n个村庄,村庄间有一些路,但有一些路可以不要也可连通所有村庄,为节约费用,deal with一些不必要的路,求最少维护总费用。

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=406

——>>用Kruskal算法生成一棵最小生成树,输出即可。

#include <iostream>
#include <queue>
#include <string.h>

using namespace std;

const int maxn = 30;        //1<n<27,开30够了

struct edge     //定义边数据类型
{
int v1;     //边的端点之一
int v2;     //边的端点之一
int cost;       //一个月维护这条路的费用
};

bool operator < (edge e1, edge e2)      //定义优先队列排序方式
{
return (e1.cost > e2.cost);
}

int fa[maxn], height[maxn];     //fa[x]为x的父亲,height[i]以i为根的树的高度(并查集)
int find(int x)     //找x的根(并查集)
{
while(fa[x] != x)
{
x = fa[x];
}
return x;
}

bool judge(int x, int y)        //判断加入边x——y后是否会开成环,会的返回0,不会的话返回1
{
int new_x = find(x);
int new_y = find(y);
if(new_x == new_y) return 0;        //会形成环
else        //不会形成环
{
if(height[new_x] > height[new_y]) fa[new_y] = new_x;
else if(height[new_x] == height[new_y])
{
fa[new_y] = new_x;
height[new_x]++;
}
else fa[new_x] = new_y;
return 1;
}
}

bool G[maxn][maxn];     //用邻接矩阵来存储图
bool vis[maxn], viss[maxn];         //vis、viss均用来判断该村庄是否走过
int n;      //n个村庄

void dfs(int i)     //深度优先遍历修改一个连通块
{
vis[i] = 1;
int j;
for(j = 1; j <= n; j++)
if(G[i][j] == 1 && vis[j] == 0)
dfs(j);
return;
}

bool is_ok()        //判断村庄是否已连通
{
memset(viss, 0, sizeof(viss));
int count = 0, ok = 1, i;
for(i = 1; i <= n; i++)
{
if(viss[i] == 0)
{
dfs(i);
count++;
if(count >= 2)
{
ok = 0;
break;
}
}
}
if(ok) return 1;
else return 0;
}

int main()
{
int i, j;
while(cin>>n)
{
if(n == 0) return 0;
char c1, c2;
int num1, num2;
for(i = 1; i <= n; i++)
{
fa[i] = i;
height[i] = 1;
}

priority_queue<edge> qu;        //用优先队列取最小费用的路径

edge newnode;
memset(G, 0, sizeof(G));
for(i = 1; i <= n-1; i++)
{
cin>>c1>>num1;
for(j = 1; j <= num1; j++)
{
cin>>c2>>num2;
G[(int)(c1-'A')+1][(int)(c2-'A')+1] = 1;        //把A,B,C...变成1,2,3...
G[(int)(c2-'A')+1][(int)(c1-'A')+1] = 1;
newnode.v1 = (int)(c1-'A')+1;
newnode.v2 = (int)(c2-'A')+1;
newnode.cost = num2;
qu.push(newnode);       //入列
}
}

int sum = 0;        //费用总数
int OK = 0;     //整个island是否连通的标志
memset(vis, 0, sizeof(vis));
while(!OK && !qu.empty())
{
if(judge(qu.top().v1, qu.top().v2))     //用并查集实现判断加入那条边是否会形成环,不会的时候
{
OK = 0;
vis[qu.top().v1] = 1;       //将这两个村庄标记为已走
vis[qu.top().v2] = 1;
sum += qu.top().cost;       //累加总费用
if(is_ok()) OK = 1;     //加入此边后判断所有村庄是否已连通
}
qu.pop();
}
cout<<sum<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: