挑战程序设计竞赛最小生成树习题(4道)及详解:C++实现

最小生成树

  • POJ 1258:Agri-Net
  • POJ 2377:Bad Cowtractors
  • POJ 2395:Out of Hay
  • AOJ 2224:Save your cats

这四道题比较基本,没有过多复杂的过程,所以整合在一篇博客,适合学过最小生成树算法后来加深理解

POJ 1258:Agri-Net

点击进入题面
最小生成树模板题,输入为图的邻接矩阵,所以优先考虑prim算法:

#include
#include
using namespace std;
const int maxx=101;
int tot;
int n;
int a[maxx][maxx];
int mincost[maxx];
bool used[maxx];
int prim()
{
	int cmp,v;
	for (int i = 0; i < n; i++)
	{
		used[i]=false;
		mincost[i]=a[0][i];
	}
	used[0]=true;
	for (int j = 1; j < n; j++)
	{
		cmp=INT_MAX;
		for (int i = 0; i < n; i++)
		{
			if(!used[i]&&mincost[i]<cmp)
			{
				cmp=mincost[i];
				v=i;
			}
		}
		tot+=cmp;
		used[v]=1;
		for (int i = 0; i < n; i++)
		{
			if(!used[i]&&a[v][i]<mincost[i]) mincost[i]=a[v][i];
		}
	}
	return tot;
}
int main()
{
	while(cin>>n)
	{
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				cin>>a[i][j];
			}
		}
		cout<<prim()<<endl;
		tot=0;
	}
	return 0;
}

当然,也可以使用Kruskal算法:

#include
#include
using namespace std;
const int maxx=101;
const int maxa=10000;
int par[maxx];
int ran[maxx];
void init_union(int n)
{
	for (int i = 1; i <= n; i++)
	{
		par[i]=i;
		ran[i]=0;
	}
}
int find(int x)
{
	return x==par[x]? x: par[x]=find(par[x]);
}
void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if (x==y) return ;
	if (ran[x]<ran[y])
	{
		par[x]=y;
	}
	else
	{
		par[y]=x;
		if(ran[x]==ran[y]) ran[x]++;
	}
}
bool same(int x,int y)
{
	return find(x)==find(y);
}
int n;
int num;
typedef struct
{
	int begin;
	int end;
	int weight;
}Edge;
Edge e[maxa];
bool cmp(Edge a,Edge b)
{
	return a.weight<b.weight;
}
int MiniSpanTree_Kruskal_UnionFind(int n)
{
	init_union(n);
	int tot=0;
	for (int i = 0; i < num; i++)
	{
		if (!same(e[i].begin,e[i].end))
		{
			unite(e[i].begin,e[i].end);
			tot+=e[i].weight;
		}
	}
	return tot;
}
int main()
{
	while(cin>>n)
	{
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				cin>>e[num].weight;
				e[num].begin=i;
				e[num].end=j;
				num++;
			}
		}
		sort(e,e+num,cmp);
		cout<<MiniSpanTree_Kruskal_UnionFind(n)<<endl;
		num=0;
	}
	return 0;
}

POJ 2377:Bad Cowtractors

点击进入题面
最大生成树,我们让Kruskal()的边集数组从大到小排列依次添加即可,最后遍历如果有图不为连通图,则输出-1:

#include
#include
using namespace std;
const int maxx=1001;
const int maxa=20001;
struct Edge
{
	int x;
	int y;
	int w;
};
Edge e[maxa];
int par[maxx];
int h[maxx];
bool cmp(Edge x,Edge y)
{
	return x.w>y.w;
}
void init(int n)
{
	for (int i = 1; i <= n; i++)
	{
		par[i]=i;
		h[i]=0;
	}
}
int find(int x)
{
	return x==par[x]? x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return ;
	if (h[x]<h[y])
	{
		par[x]=y;
	}
	else
	{
		par[y]=x;
		if(h[x]==h[y]) h[x]++;
	}
}
bool same(int x,int y)
{
	return find(x)==find(y);
}
int n,m;
int kruskal()
{
	init(n);
	int tot=0;
	for (int i = 0; i < m; i++)
	{
		if(!same(e[i].x,e[i].y))
		{
			unite(e[i].x,e[i].y);
			tot+=e[i].w;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = i+1; j <= n; j++)
		{
			if(!same(i,j)) return -1;
		}
	}
	return tot;
}
int main()
{
	while(cin>>n>>m)
	{
		for (int i = 0; i < m; i++)
		{
			cin>>e[i].x>>e[i].y>>e[i].w;
		}
		sort(e,e+m,cmp);
		cout<<kruskal()<<endl;
	}
	return 0;
}

POJ 2395:Out of Hay

点击进入题面
求最小生成树中的两节点间最大路径,在构建过程中不断更新添加的边的最大值即可:

#include
#include
using namespace std;
const int maxx=2001;
const int maxa=10001;
struct Edge
{
	int x;
	int y;
	int w;
};
Edge e[maxa];
int par[maxx];
int h[maxx];
bool cmp(Edge x,Edge y)
{
	return x.w<y.w;
}
void init(int n)
{
	for (int i = 1; i <= n; i++)
	{
		par[i]=i;
		h[i]=0;
	}
}
int find(int x)
{
	return x==par[x]? x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return ;
	if (h[x]<h[y])
	{
		par[x]=y;
	}
	else
	{
		par[y]=x;
		if(h[x]==h[y]) h[x]++;
	}
}
bool same(int x,int y)
{
	return find(x)==find(y);
}
int n,m;
int kruskal()
{
	init(n);
	int tot=0;
	for (int i = 0; i < m; i++)
	{
		if(!same(e[i].x,e[i].y))
		{
			unite(e[i].x,e[i].y);
			if(e[i].w>tot) tot=e[i].w;
		}
	}
	return tot;
}
int main()
{
	while(cin>>n>>m)
	{
		for (int i = 0; i < m; i++)
		{
			cin>>e[i].x>>e[i].y>>e[i].w;
		}
		sort(e,e+m,cmp);
		cout<<kruskal()<<endl;
	}
	return 0;
}

AOJ 2224:Save your cats

点击进入题面
这道题稍微绕一点,首先各边的权值需要自己算,然后求的是最小花费=总权值-最大生成树总权值
别忘了节点编号从1开始,因为这个卡了好久…

#include
#include
#include
#include
using namespace std;
const int maxx=10001;
const int maxa=1e8+1;
struct Edge
{
	int x;
	int y;
	double w;
};
Edge e[maxa];
int par[maxx];
int h[maxx];
bool cmp(Edge x,Edge y)
{
	return x.w>y.w;
}
void init(int n)
{
	for (int i = 1; i <= n; i++)
	{
		par[i]=i;
		h[i]=0;
	}
}
int find(int x)
{
	return x==par[x]? x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return ;
	if (h[x]<h[y])
	{
		par[x]=y;
	}
	else
	{
		par[y]=x;
		if(h[x]==h[y]) h[x]++;
	}
}
bool same(int x,int y)
{
	return find(x)==find(y);
}
int n,m;
double kruskal()
{
	init(n);
	double er=0;
	for (int i = 0; i < m; i++)
	{
		if(!same(e[i].x,e[i].y))
		{
			unite(e[i].x,e[i].y);
			er+=e[i].w;
		}
	}
	return er;
}
pair<int,int> a[maxx];
double exchange(int x,int y)
{
	return sqrt((a[x].first-a[y].first)*(a[x].first-a[y].first)+(a[x].second-a[y].second)*(a[x].second-a[y].second));
}
int main()
{
	cin>>n>>m;
	double tot=0;
	for (int i = 0; i < n; i++)
	{
		cin>>a[i].first>>a[i].second;
	}
	for (int i = 0; i < m; i++)
	{
		cin>>e[i].x>>e[i].y;
		e[i].w=exchange(e[i].x-1,e[i].y-1);
		tot+=e[i].w;
	}
	sort(e,e+m,cmp);
	printf("%.3lf\n",tot-kruskal());
	return 0;
}

你可能感兴趣的:(图论,挑战程序设计竞赛,图论,kruskal,prim,算法,c++)