您的位置:首页 > 其它

HDOJ 1233 还是畅通工程(并查集、kruscal算法 + prim)

2014-04-19 21:33 393 查看
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1233

这是我第一次接触到算法~~想想还有点小激动诶~
这个题就是一个简单的最小生成树。
会想到去写这道题是因为我写了1232,那是一个简单的并查集的题目。水过1232之后,就顺便看了看1233。结果,这题纠结了我好久啊!!抓狂!!后面龙酱帮我捉虫的时候才发现竟然是并查集写错了。。重写一遍1232去QAQ
其实是因为不理解并查集啦。。当时死记硬背,只是把并查集的框架背了下来,结果背错了ORZ..
1233题因为是用的kruskal,所以会用到并查集。最小生成树还可以用prime算法做,但是我还没看0.0
so,what is bingchaji?(请首先阅读并查集的百度百科或者维基百科)
首先并查集有个father数组存储每个元素的元祖。father数组的下标代表元素,里头则存储着他们的老爸(当然老爸和自己是一个元祖)或者元祖,比如元素i的祖先是j,则表示为father[i] = j;如果该元素就是元祖,则存储自己,如father[j] = j。

然后还有一个find函数,用来寻找元祖的。

int find(int i)
{
if (i == father[i])
return i; //找到元祖
else
return father[i] = find(father[i]); //存储数与下标不相等,还没有找到元祖~~~
}


似乎还可以路径压缩,下次琢磨一下。

kruskal算法就是先把边存起来,然后按边权排序,再利用并查集查询是否同一元祖(或者两个朋友or亲戚有没有关系、能不能从别的路径走通两个村庄)按照顺序来一个个挑选是否构建这条边。如果同意元祖,不构建;若非同一元祖,构建。

因为已经按边权排序,所以构建边的时候边权是递增的,当所有元素都连接时,总边权应为最小(因为是贪心,所以如果有那种比较厉害的数据可能过不了)。

下面是ac代码:

#include<stdio.h>
#include<stdlib.h>

int father[100];
//用结构体来做方便排序
struct node
{
int u; //村庄一号
int v; //村庄二号
int dis; //公路长,即边权
}x[5000];

int find(int i)
{
if (i == father[i])
return i; //找到元祖
else
return father[i] = find(father[i]); //存储数与下标不相等,还没有找到元祖
}

int cmp(const void *a, const void *b) //qsort的比较函数,这个表示从小到大排。
{
return (*(node *)a).dis > (*(node *)b).dis ? 1 : -1; //下次要排别的变量,只要改掉node和.dis就可以了。至于为什么是1和-1的返回值,不知道,背的..T^T
}

int main()
{
int N;

x[0].dis = 0; //防止这个不用的dis因为没初始化结果在排序的时候乱入

//freopen("1233.txt", "r", stdin);

while(scanf("%d", &N) != EOF && N)
{
int i;
int road; //总共可修的公路条数
int sum = 0; //总边权
int m = 0; //实际修路数,计数用

road = N * (N-1) / 2;
sum = 0;

for (i = 1; i <= N; i++)
father[i] = i; //初始化father数组

for (i = 1; i <= road; i++)
scanf("%d%d%d", &x[i].u, &x[i].v, &x[i].dis); //储存边

qsort(x+1, road, sizeof(x[0]), cmp); //按边权排序

for (i = 1; i <= road ;i++)
{
int s1 = find(x[i].u);
int s2 = find(x[i].v);
if (s1 != s2) //如果两个村庄不修路就无法联通,即元祖不同
{
father[s1] = father[s2]; //把前者的元祖的father元素里存的自己改成后者的元祖
sum += x[i].dis;
m++; //修成了一条路!
}
}
printf("%d\n", sum);
}
return 0;
}

补上prim的解法:
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 1010

int cost[MAXN][MAXN];
bool vis[MAXN];
int low[MAXN];
int u, v, c;

int prim(int cost[][MAXN], int n)
{
int ans = 0;
memset(vis, false, sizeof(vis));
vis[0] = true;
for(int i = 1; i < n; i++)
low[i] = cost[0][i];
for(int i = 1; i < n; i++)
{
int minc = INF;
int p = -1;
for(int j = 0; j < n; j++)
if((!vis[j]) && (minc > low[j]))
{
minc = low[j];
p = j;
}
if(minc >= INF)
return -1;
ans += minc;
vis[p] = true;
for(int j = 0; j < n; j++)
if((!vis[j]) && (low[j] > cost[p][j]))
low[j] = cost[p][j];
}
return ans;
}

int main()
{
//    freopen("1233.in", "r", stdin);

int n;
while(scanf("%d", &n), n)
{
int se = (n - 1) * n / 2;
memset(cost, 0x3f, sizeof(cost));
for(int i = 0; i < se; i++)
{
scanf("%d%d%d", &u, &v, &c);
cost[u-1][v-1] = c;
cost[v-1][u-1] = c;
}
printf("%d\n", prim(cost, n));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: