POJ1258 Agri-Net(最小生成树)
题意:
市长要将每个村庄都连通起来,要求村庄之间路的总和最短
要点:
学了一下最小生成树,目前只会Kruskal算法,这个算法的基本思路是先按边的权值,从小到大排列,然后每次将边的起点和终点放进集合中,直至所有节点都在一个连通分量中。既然是连通分量,那就正好可以用刚学的并查集。如果这条边的两个节点通过find发现相同了,说明这两个已经在集合中了。最后并查集也可以看成一棵树,与我前面做过的一道题一样,只要边数为节点数-1即可,这也就是最小生成树。
参考博客:最小生成树
Kruskal算法:
15338787 | Seasonal | 1258 | Accepted | 240K | 16MS | C++ | 1001B | 2016-03-31 22:09:17 |
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct edge
{
int u, v, len;
}e[150*150];
int p[150];
int n,num;
bool cmp(const edge &a, const edge &b)
{
return a.len < b.len;//根据权值从小到大排列
}
void init()
{
for (int i = 0; i < n; i++)
p[i] = i;
}
int find(int x)
{
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
bool merge(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)//不相等说明无环图
{
p[x] = y;
return true;
}
return false;
}
int kruskal()
{
int sum = 0, edges = 0;;
init();
sort(e, e + num, cmp);
for (int i = 0; i < num; i++)
{
if (merge(e[i].u, e[i].v))//将边按权值从小到大生成一棵树,利用并查集
{
sum += e[i].len;
edges++;
}
if (edges + 1 == n)//树的边总数应是结点总数-1
return sum;
}
return -1;
}
int main()
{
int temp,i,j;
while (~scanf("%d", &n))
{
num = 0; //用num记录总共有几条边
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
{
scanf("%d", &temp);
if (i != j)
{
e[num].u = i;
e[num].v = j;//记录起点和终点
e[num++].len = temp;//记录对应权值
}
}
printf("%d\n",kruskal());
}
return 0;
}
Prim算法:
15341439 | Seasonal | 1258 | Accepted | 208K | 16MS | C++ | 737B | 2016-04-01 19:58:47 |
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define INF 0xffffff
int n;
int map[105][105],low[105];
bool vis[105];
int prim()
{
memset(vis, false, sizeof(vis));
int i, j, mark, min,sum=0;
vis[1] = true;
for (i = 1; i <= n; ++i)
low[i] = map[1][i];
for (i = 1; i < n; ++i)
{
min = INF;
for (j = 1; j <= n; ++j)
{
if (!vis[j] && low[j]<min)
{
min = low[j];
mark = j;
}
}
sum += min;
vis[mark] = true;
for (j = 1; j <= n; ++j)
if (!vis[j] && map[mark][j] < low[j])
low[j] = map[mark][j];
}
return sum;
}
int main()
{
while (~scanf("%d", &n))
{
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &map[i][j]);
printf("%d\n", prim());
}
return 0;
}