最短路径 最小生成树 堆
求最短路径
直接来个模板题目
最短路
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 11 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是保存的和树的距离
堆
堆就是一颗满足所有根节点大于(小于)子结点完全二叉树
我们用以为数组来模拟这个数据结构
上题目吧
题目描述
利用快速排序算法将读入的 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;
}