生成树题目

时间:2020-03-24
本文章向大家介绍生成树题目,主要包括生成树题目使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1486:【例题1】黑暗城堡

先用dijkstra求出1号房间到每个房间的单源最短路径存储到dis数组中。把树形城堡看作以1为根的有根树。由题,若x是y的根节点,x、y之间的通道长度为z,
则应该有:dis[y]=dis[x]+z。事实上,我们把满足题目要求的树结构,即对任意一对父子结点x、y都有上式成立的树结构,称为图的一棵最短路径生成树。
与Prim算法类似,统计有多少结点x满足dis[p]=dis[x]+e[x][p],让p与其中任意一个x相连都符合题目要求

最短路径生成树:对于任意一对父子结点x、y均满足dis[y]=dis[x]+e[x][y]的树结构称为图的一棵最短路径生成树;

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;\
/*
先用dijkstra求出1号房间到每个房间的单源最短路径存储到dis数组中。把树形城堡看作以1为根的有根树。由题,若x是y的根节点,x、y之间的通道长度为z,
则应该有:dis[y]=dis[x]+z。事实上,我们把满足题目要求的树结构,即对任意一对父子结点x、y都有上式成立的树结构,称为图的一棵最短路径生成树。
与Prim算法类似,统计有多少结点x满足dis[p]=dis[x]+e[x][p],让p与其中任意一个x相连都符合题目要求

最短路径生成树:对于任意一对父子结点x、y均满足dis[y]=dis[x]+e[x][y]的树结构称为图的一棵最短路径生成树;
*/
LL  dis[maxn];
LL num[maxn];
int vis[maxn];
struct node{
	int to,val;
	node(int a,int b){
		to=a;val=b;
	}
};
vector<node> adj[maxn];
int n,m;
int main(){
	int x,y,z;
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d %d %d",&x,&y,&z);
		adj[x].push_back(node(y,z));
		adj[y].push_back(node(x,z));
	}
	for(int i=1;i<=n;i++) dis[i]=INF;
	dis[1]=0;
	for(int i=1;i<=n;i++){
		int u=-1,temp=INF;
		for(int j=1;j<=n;j++){
			if(!vis[j]&&dis[j]<temp){
				temp=dis[j];
				u=j;
			}
		}
		if(u==-1) break;
		vis[u]=1;
		for(int j=0;j<adj[u].size();j++){
			int diss=adj[u][j].val;
			int to=adj[u][j].to;
			if(!vis[to]){
				if(dis[to]>dis[u]+diss){
					dis[to]=dis[u]+diss;
				}
			}
		}
	}
	
	LL ans=1,mod=(1<<31)-1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<adj[i].size();j++){
			int t=adj[i][j].to;
			if(dis[t]==dis[i]+adj[i][j].val) num[t]++;
		}
	} 
	for(int i=1;i<=n;i++){
		//cout<<num[i]<<endl;
		if(num[i]){
			ans=(ans*num[i])%mod;
		}
	}
	printf("%lld\n",ans);
return 0;
}

1487:【例 2】北极通讯网络

 题目的意思是求最小生成树的第k大的边的长度

k个村庄装上卫星设备后可以看成1个点,那么只需要求这(n-k+1)个点和(n-k)条边所构成的最小生成树
利用Kruskal算法,只需要进行(n-k)次操作,最后一次操作时加入生成树的边的权值就是所求的d

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//题目的意思是求最小生成树的第k大的边的长度
/*
k个村庄装上卫星设备后可以看成1个点,那么只需要求这(n-k+1)个点和(n-k)条边所构成的最小生成树
利用Kruskal算法,只需要进行(n-k)次操作,最后一次操作时加入生成树的边的权值就是所求的d
*/
int n,k,m,fa[510],x[510],y[510];
struct node{
	int a,b;
	double dis;
}ed[250000];
double js(int ax,int ay,int bx,int by){
	return sqrt(pow(ax-bx,2)+pow(ay-by,2));
}
bool cmp(node a,node b){
	return a.dis<b.dis;
}
int findf(int x){
	if(x==fa[x]) return x;
	else return fa[x]=findf(fa[x]);
}
int num1;
double res;
void kruskal(){
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		int fa1=findf(ed[i].a);
		int fa2=findf(ed[i].b);
		if(fa1!=fa2){
			fa[fa1]=fa2;
			num1++;
			if(num1==n-k){
				res=ed[i].dis;
				return ;
			}
		}
	}
}
int main(){
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++){
			scanf("%d %d",&x[i],&y[i]);
		}
		if(k==0) k=1; //别忘了这一个 
	if(k>=n) {
		printf("0.00\n");
		return 0;
	}
	else{
		
		m=1;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				ed[m].a=i;
				ed[m].b=j;
				ed[m].dis=js(x[i],y[i],x[j],y[j]);
				m++;
			}
		}
		m--;  //细节呀!!!!!!!!! 
		sort(ed+1,ed+m+1,cmp);
		kruskal();
		printf("%.2lf\n",res);
	}
return 0;
}

1488:新的开始

需要加一个超级源点!!!!!加的超级源点与各个点的边权为建立收费站的价值

应该建一个虚拟源点,每个点i连向虚拟源点的边权为发电站费用v[ i ]

为什么呢..?因为虽然是双向边,但存边的时候只能单向存。

这就导致,从哪个点出发,会决定了,有的边加不加的进去。

于是...不能简简单单的取出发电站费用中最少的.

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//还是不太理解为什么要加一个超级源点的问题
int n;
int mon[510];
int mp[510][510];
LL dis[510];
int vis[510]={0};
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>mon[i];
		mp[i][1+n]=mon[i];
		mp[1+n][i]=mon[i];
	} 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>mp[i][j];
		}
	}
	for(int i=1;i<=n+1;i++){   //超级源点也要加上 
		dis[i]=mp[1][i];
	}
	LL ans=0;
	vis[1]=1;
	for(int i=1;i<=n;i++){
		int u=-1,temp=INF;
		for(int j=1;j<=n+1;j++){
			if(!vis[j]&&dis[j]<temp){
				temp=dis[j];
				u=j;
			}
		}
		if(u==-1) break;
		vis[u]=1;
		ans+=dis[u];
		for(int j=1;j<=n+1;j++){
			if(!vis[j]&&j!=u&&dis[j]<mp[u][j]){
				dis[j]=mp[u][j];
			}
		}
	}
	cout<<ans<<endl;
return 0;
}

原文地址:https://www.cnblogs.com/shirlybaby/p/12558967.html