堆,和最小生成树,和最短路径小总结

最短路径 最小生成树 堆

求最短路径

直接来个模板题目

最短路

Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

 

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
 

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
 

Sample Input
2 1

1 2 3

3 3

1 2 5

2 3 5

3 1 2

0 0
 

Sample Output
3 2

首先我们得学会建图

用一个二维数组建图

规定map[i][j]记录顶点i到顶点j的最短路径这样就可以直接用用map访问i和j的距离了

这里是求1到n的最短路

迪杰斯塔拉算法来解决单源最短路

算法原理就是用dis数组来记录所用顶点和1的估算距离(1)

然后每次找距离1号顶点最近且上次没有确定的点作为确定值 来松弛这个顶点可联通的点和1号点的距离(2)

重复一二即可

ac代码如下

#include<iostream>
	#include<algorithm>
	#include<cstring>
	#include<cstdio>
	using namespace std;
	int map1[2005][2005];
	int book[2005];
	int dis[2005]; 
	int inf=99999999;
	int main()
	{
		int i;
		int n;
		int m;
		while(~scanf("%d%d",&n,&m))//n个公交车站
		{
			if(m+n==0)break;
			memset(map1,0,sizeof(map1));
			memset(book,0,sizeof(book));
			
			//m条边
		 for(int i=1;i<=n;i++)
		 {
		 	for(int j=1;j<=n;j++) 
		 	{
		 		if(i==j)map1[i][j]=0;
		 		else map1[i][j]=inf;
			 }
		 }
		 for(int i=1;i<=m;i++)
		 {
		 	//for(int j=1;j<=n;j++)
		 	
		 		int a,b,c;
		 		cin>>a>>b>>c;
		 		map1[b][a]=map1[a][b]=min(map1[a][b],c);

			 
		 }
		 book[1]=1;
		 for(int i=1;i<=n;i++)//m条边 
		 dis[i]=map1[1][i];
		 for(int i=1;i<n;i++)//1是确定的
		 {
		 	int min1=inf;
		 	int k;
		 	for(int j=1;j<=n;j++)//找确定值 
			 {
			 	if(dis[j]<min1&&book[j]==0)
			 	{
			 		min1=dis[j];
			 		k=j;
				 }
			 }
			 book[k]=1;//标记确定值 
			  for(int v=1;v<=n;v++)
			  {
			  	if(map1[k][v]>=inf||book[v])//v确定了 就不用跟新了 或者走不通也不要更新 
			  	continue;
			  	dis[v]=min(dis[v],dis[k]+map1[k][v]);
			   } 
			    
		  } 
	//	  for(int i=1;i<=n;i++)
	//	  cout<<dis[i]<<endl;
		 cout<<dis[n]<<endl;
		  
		}
		return 1;
	 } 

最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式

第一行包含两个整数 N,MN,M,表示该图共有 NN 个结点和 MM 条无向边。

接下来 MM 行每行包含三个整数 X_i,Y_i,Z_iXi​,Yi​,Zi​,表示有一条长度为 Z_iZi​ 的无向边连接结点 X_i,Y_iXi​,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

输入输出样例

输入 #1复制

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出 #1复制

7

说明/提示

数据规模:

对于 20\%20% 的数据,N\le 5N≤5,M\le 20M≤20。

对于 40\%40% 的数据,N\le 50N≤50,M\le 2500M≤2500。

对于 70\%70% 的数据,N\le 500N≤500,M\le 10^4M≤104。

对于 100\%100% 的数据:1\le N\le 50001≤N≤5000,1\le M\le 2\times 10^51≤M≤2×105。

样例解释:

所以最小生成树的总边权为 2+2+3=72+2+3=7。

洛谷上面的题目

这有很多解法

但是我只会一种 就是和迪杰斯特拉算法很类似的prim

思路是这样的 dis数组来记录距离这课树最近的距离 然后每次找距离这颗树最近的顶点来连接

ac代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int m;
int dis[5005];
int map[5005][5005];
int book[5005];
int inf=0x3ffffff; 
int main()
{
	int n,m;
	cin>>n>>m;
	//初始化图
	 for(int i=1;i<=n;i++)
	 {
	 	for(int j=1;j<i;j++)
	 	map[i][j]=map[j][i]=inf;
		// map[i][i]=0;//因为是全局变量可以不初始map[i][i]=0;  
	 }
	 while(m--)
	 {
	 	int a,b,c;
	 	cin>>a>>b>>c;
	 	if(map[a][b]>c)map[a][b]=map[b][a]=c;
	 }
	  book[1]=1;//这里选择1号顶点开始生成树 
	 for(int i=1;i<=n;i++)
	 dis[i]=map[i][1];//记录所有结点和树的估算值	 
	 int count=1;//树的联通的顶点数 
	 int sum=0;//路径之和
	 while(count<n)
	 {
	 	int min1=inf;
	 	int k=inf;
	 	for(int i=1;i<=n;i++)//找距离树最近的顶点 
	 	{
	 		if(min1>dis[i]&&book[i]==0)//到未加入树当中找最小值 
	 		{
	 			min1=dis[i];
	 			k=i;
			 }
		 } 
			 if(k==inf)break;//如果不连通了 那么就可以打断了 
			 book[k]=1;//标记加入树的顶点
			 count++;
			 sum+=dis[k];
			 for(int j=1;j<=n;j++) 
			 if(book[j]==0&&map[k][j]<dis[j])//看能不能通过k这顶点使其他顶点到这个树的距离变短 
			 dis[j]=map[k][j]; 
			
		 
	  } 
	  if(count==n)cout<<sum<<endl;
	  else cout<<"orz"<<endl; 
}

prim和迪杰斯特拉算法很想

可以说思路是一样的 唯一区别是一个dis数据保存的是不同

迪杰斯特保存的是和出发地的距离

而prim是保存的和树的距离

 

堆就是一颗满足所有根节点大于(小于)子结点完全二叉树

我们用以为数组来模拟这个数据结构

上题目吧

P1177 【模板】快速排序

题目描述

利用快速排序算法将读入的 NN 个数从小到大排序后输出。

快速排序是信息学竞赛的必备算法之一。对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成。(C++C++ 选手请不要试图使用 STL,虽然你可以使用 sort 一遍过,但是你并没有掌握快速排序算法的精髓。)

输入格式

第 11 行为一个正整数 NN,第 22 行包含 NN 个空格隔开的正整数 a_iai​,为你需要进行排序的数,数据保证了 A_iAi​ 不超过 10^9109。

输出格式

将给定的 NN 个数从小到大输出,数之间空格隔开,行末换行且无空格。

输入输出样例

输入 #1复制

5
4 2 4 5 1

输出 #1复制

1 2 4 4 5

说明/提示

对于 20\%20% 的数据,有 N\leq 10^3N≤103;

对于 100\%100% 的数据,有 N\leq 10^5N≤105。

我们用堆排序来写这个快排

不接受太多了 上代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int h[100006];
int n;
int swap(int x,int y)
{
	h[x]=h[x]^h[y];
	h[y]=h[x]^h[y];
	h[x]=h[x]^h[y];
}
void siftdown(int i)//向下调整  把小的放上来 
{
	while(i*2<=n)
	{
		int t1=i*2;
		int t2=i*2+1;
		int t;
		if(t2<=n)//如果有右儿子 就要考虑右儿子 
		{
			if(h[t1]>h[t2])t=t2;
			else t=t1;
			if(h[t]>h[i])t=i;

			
		}
		else if(h[t1]>h[i])t=i;//没有右儿子 只考虑左儿子 
		else t=t1;
		if(i==t)break;//如果本身是最小的就表示调整完了 
		else {
			swap(t,i);
			i=t;
		} 
	}	
}
int de()
{
	int ans=h[1];
	h[1]=h[n];
	n--;
	siftdown(1);
	return ans; 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>h[i];
	}
	for(int i=n/2;i>=1;i--)//建立最小堆  一共有n个结点 那么n/2是最小的非叶子结点 向下调整所有非叶子结点 
	siftdown(i);//最小堆 
	int m=n;
	for(int i=1;i<=m;i++)//取出当前最小数 输出 
	cout<<de()<<" ";
	cout<<endl;
 } 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值