集训第一天
莫名其妙把并查集和最小生成树一起学习了一下,并查集倒是经常会用到克鲁斯卡尔算法,挺好的
最小生成树Prim算法实现
(所谓生成树,就是n个点之间连成n-1个点的图形,而最小生成树,就是权值之和的最小值。)
用二维数组记录点和权值。如上图所示无向图:
int map[7][7];
map[1][2]=map[2][1]=4;
map[1][3]=map[3][1]=2;
......
然后再求最小生成树。具体方法是:
1.先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。
visited[1]=1;
pos=1;
//用low[]数组不断刷新最小权值,low[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。
low[1]=0; //起始点i到邻近点的最小距离为0
low[2]=map[pos][2]=4;
low[3]=map[pos][3]=2;
low[4]==map[pos][4]=3;
low[5]=map[pos][5]=MaxInt; //无法直达
low[6]=map[pos][6]=MaxInt;
2.再在伸延的点找与它邻近的两者权值最小的点。
//low[]以3作当前位置进行更新
visited[3]=1;
pos=3;
low[1]=0; //已标记,不更新
low[2]=map[1][2]=4; //比5小,不更新
low[3]=2; //已标记,不更新
low[4]=map[1][4]=3; //比1大,更新后为:low[4]=map[3][4]=1;
low[5]=map[1][5]=MaxInt;//无法直达,不更新
low[6]=map[1][6]=MaxInt;//比2大,更新后为:low[6]=map[3][6]=2;
所有权值相加就是最小生成树,其值为2+1+2+4+3=12。
POJ1258
Sample Input
4 0 4 9 21 4 0 8 17 9 8 0 16 21 17 16 0
Sample Output
28
题意: 给你N*N矩阵,表示N个村庄之间的距离。FJ要把N个村庄全都连接起来,求连接的最短距离。
下面是代码,很详细
#include <stdio.h>
#include <string.h>
#define MaxInt 0x3f3f3f3f
#define N 110
//创建map二维数组储存图表,low数组记录每2个点间最小权值,visited数组标记某点是否已访问
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
//从某点开始,分别标记和记录该点
visited[1]=1;pos=1;
//第一次给low数组赋值
for(i=1;i<=n;i++)
if(i!=pos) low[i]=map[pos][i];
//再运行n-1次
for(i=1;i<n;i++)
{
//找出最小权值并记录位置
min=MaxInt;
for(j=1;j<=n;j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];pos=j;
}
//最小权值累加
result+=min;
//标记该点
visited[pos]=1;
//更新权值
for(j=1;j<=n;j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return result;
}
int main()
{
int i,v,j,ans;
while(scanf("%d",&n)!=EOF)
{
//所有权值初始化为最大
memset(map,MaxInt,sizeof(map));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
scanf("%d",&v);
map[i][j]=map[i][j]=v;
}
ans=prim();
printf("%d\n",ans);
}
return 0;
}
最小生成树Kruskal算法实现
并查集的精妙之处在于用数来表示集合。如果把x的父结点保存在p[x]中(如果没有父亲,p[x]=x),则不难写出结点x所在树的递归程序:
find(int x) {return p[x]==x?x:p[x]=find(p[x]);}
意思是,如果p[x]=x,说明x本身就是树根,因此返回x;否则返回x的父亲p[x]所在树的根结点。
既然每棵树表示的只是一个集合,因此树的形态是无关紧要的,并不需要在“查找”操作之后保持树的形态不变,只要顺便把遍历过的结点都改成树根的儿子,下次查找就会快很多了。
模板代码
<pre name="code" class="cpp">struct KRUSKAL
{
const int MAXN = 109;
const int MAXE = 5009;
struct EDGE
{
int u, v, length, choose;
} edge[ MAXE ];
int path[ MAXN ];
int N, edgecnt, sum;
void Addedge(int u, int v, int len)
{
++edgecnt;
edge[ edgecnt ].u = u;
edge[ edgecnt ].v = v;
edge[ edgecnt ].length = len;
edge[ edgecnt ].choose = false;
return ;
}
void Set()
{
for (int i = 1; i <= N; i++)
path[i] = i;
return ;
}
int Find_Path(int x)
{
if (x != path[x]) path[x] = Find_Path( path[x] );
return path[x];
}
int Work()
{
int cnt = 0, x, y;
Qsort(1, edgecnt); // i < j -> edge[i].length < edge[j].length
Set();
for (int i = 1; i <= E && cnt < N - 1; i++)
{
x = Find_Path( edge[i].u );
y = Find_Path( edge[i].v );
if (x == y) continue;
path[x] = y;
edge[i].choose = true;
++cnt;
sum += edge[i].length;
}
return sum;
}
} Kruskal;
HDU 1863(基础模板题)一系列畅通工程
//***************************************
// hdu 1863 (kruskal)
// 题意:路修通成本最低
//
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
struct Fuck
{
int u,v,w;
}fuck[110];
int p[110],m,n,sum;
int cmp(Fuck x,Fuck y)
{
return x.w<y.w;
}
int find(int x)
{
return x==p[x]?p[x]:find(p[x]);
}
void Kruskal()
{
int i,ans=0;
for(i=1;i<=m;i++)
p[i]=i;
sort(fuck+1,fuck+n+1,cmp);
for(i=1;i<=n;i++)
{
int x=find(fuck[i].u);
int y=find(fuck[i].v);
if(x!=y)
{
sum++;
ans+=fuck[i].w;
p[x]=y;
}
}
if(sum==m)
printf("%d\n",ans);
else
printf("?\n");
}
int main()
{
while(cin>>n>>m,n)
{
int i;
sum=1;
for(i=1;i<=n;i++)
scanf("%d%d%d",&fuck[i].u,&fuck[i].v,&fuck[i].w);
Kruskal();
}
return 0;
}
<span style="font-size:24px;"><strong>HDU1879</strong></span>
<pre name="code" class="cpp">//*********************************************
// hdu 1879 (kruskal)
//加了有无修过,修过的成本就是0,修过是1。遍历所有点即可 ,这种想法比较好写。但这里写的是修过的连起来,没修过的加一起
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<iostream>
#include<algorithm>
const int maxn=110000;
using namespace std;
struct Point
{
int u,v,w,vis;
}point[11000];
int p[110],n,m,i;
bool cmp(Point x, Point y)
{
if(x.vis!=y.vis)
return x.vis<y.vis;
else return x.w<y.w;
}
int find(int x)
{
return x==p[x]?p[x]:find(p[x]);
}
void kruskal()
{
int ans=0,i;
sort(point+1,point+1+m,cmp);
for(i=1;i<m;i++)
{
int x=find(point[i].u);
int y=find(point[i].v);
if(x!=y&&!point[i].vis)
{
ans+=point[i].w;
p[x]=y;
}}
printf("%d\n",ans);
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
p[x]=y;
}
int main()
{
while(scanf("%d",&n)&&n)
{
int i;
m=n*(n-1)/2;
for(i=1;i<=n;i++)
p[i]=i;
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&point[i].u,&point[i].v,&point[i].w,&point[i].vis);
if(point[i].vis==1)
merge(point[i].u,point[i].v);
}
kruskal(); }
}