第五章
图的遍历最短路
一.搜索
1.Floyd
2.Dijkstra
3.Bellman-ford
从理解的难度来划分2 3 1 4
Floyd借助的是数组表示
图形的遍历分为有向图和无向图
有向图输入城市a,b之间的距离是c时
Maps【a】【b】=c
无向图
Maps【a】【b】=maps【b】【a】=c
Floyd
注意路的合并
因为直达的路不一定是最短的
所以要考虑合并问题
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
maps[j][i]=maps[i][j]=min(maps[i][j],maps[i][k]+maps[k][j]);
}
}
}
利用搜索,floyd,dij。。。可以解决 权值为正的情况
搜索
最少转机问题
最简单的方法是floyd
也可以练习自己的思维用广度优先搜索
书上的内容是利用数组模拟队列的方式
数组模拟祖先是并查集的基本思路
数组的各种灵活应用是各种算法的基础
因为书上有模拟出的代码
我这里用队列深搜标准模板套一下
#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
#define num 99999999
using namespace std;
struct nana
{
int x,step;
}pre[100003];
int maps[103][103];
bool book[103][103];
int n,m,s,e;
int dfs(nana first)
{
queue<nana>q;
q.push(first);
while(!q.empty())
{
nana next=q.front();
if(next.x==e)
{
return next.step;
}
q.pop();
for(int i=1;i<=n;i++)
{
if(maps[next.x][i]!=num&&book[next.x][i])
{
nana second;
second.x=i;
second.step=next.step+1;
book[second.x][i]=false;
q.push(second);
}
}
}
}
int main()
{
while(~scanf(“%d%d%d%d”,&n,&m,&s,&e))
{
memset(book,true,sizeof(book));
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i==j)
maps[i][j]=0;
else maps[i][j]=num;
}
}
for(int i=0;i<m;i++)
{
int a,b;
scanf(“%d%d”,&a,&b);
maps[b][a]=maps[a][b]=1;
}
nana x;
x.step=0;
x.x=s;
int ans=dfs(x);
printf(“%d\n”,ans);
}
}
/*
5 7 1 5
1 2
1 3
2 3
2 4
3 4
3 5
4 5
*/
二.Dijkstra算法
单元最短路问题
我们要求的是从一个点出发到其他点的最短路问题
这种问题我们用floyd也可以处理,不过相比于dijkstra算法要费时间
我们这里先将每个点未经转折到达起点(源点)的距离存储到新的数组中
for(int i=1; i<=n; i++)
{
dis[i]=maps[1][i];
}
输入
for(int i=0; i<m; i++)
{
int a,b,c;
scanf(“%d%d%d”,&a,&b,&c);
maps[a][b]=c;
}
查找最近点并以他为中转点
for(int j=1; j<=n; j++)
{
if(m_min>dis[j]&&!book[j])
{
m_min=dis[j];
flag=j;
}
}
book[flag]=1
判断经由该点以后是否会缩短路程;
for(int j=1; j<=n; j++)
{
if(dis[flag]+maps[flag][j]<dis[j])
dis[j]=dis[flag]+maps[flag][j];
}
算法模板
void dijkstra()
{
for(int i=1; i<=n; i++)
{
dis[i]=maps[1][i];
}
book[1]=1;
for(int i=1; i<n; i++)
{
int m_min=num;
int flag=0;
for(int j=1; j<=n; j++)
{
if(m_min>dis[j]&&!book[j])
{
m_min=dis[j];
flag=j;
}
}
book[flag]=1;
for(int j=1; j<=n; j++)
{
if(dis[flag]+maps[flag][j]<dis[j])
dis[j]=dis[flag]+maps[flag][j];
}
}
}
实战应用。。。自带样例。。。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
int num=9999999;
using namespace std;
int maps[103][103];
int dis[103],book[103];
int n,m;
void dijkstra()
{
for(int i=1; i<=n; i++)
{
dis[i]=maps[1][i];
}
book[1]=1;
for(int i=1; i<n; i++)
{
int m_min=num;
int flag=0;
for(int j=1; j<=n; j++)
{
if(m_min>dis[j]&&!book[j])
{
m_min=dis[j];
flag=j;
}
}
book[flag]=1;
for(int j=1; j<=n; j++)
{
if(dis[flag]+maps[flag][j]<dis[j])
dis[j]=dis[flag]+maps[flag][j];
}
}
}
int main()
{
while(~scanf(“%d%d”,&n,&m))
{
memset(book,0,sizeof(book));
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(i==j)
maps[i][j]=0;
else maps[i][j]=num;
for(int i=0; i<m; i++)
{
int a,b,c;
scanf(“%d%d%d”,&a,&b,&c);
maps[a][b]=c;
}
dijkstra();
for(int i=1; i<=n; i++)
{
printf(“%d “,dis[i]);
}
}
}
/*
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
*/
三.邻接表
四.Bellman-ford
解决最短路径的负权值问题。
原理与dijkstra差不多
同样是松弛原则
不断地松弛。
缩短
不过因为存在负权值问题所以这里需要松弛n-1次
我们依旧是dis【103】数组存储到1的距离
a【i】b【i】存储关系
c【i】代表两点的距离
核心代码松弛n-1次
For(int i=1;i<n;i++)
{for(int i=1;i<=m;i++)
{if(dis[b[i]]>dis[a[i]]+c[i])
Dis[b[i]]=dis[a[i]]+c[i];
}
}
初始化
for(int i=1;i<=n;i++)
dis[i]=0x1f1f1f;
dis[1]=0;
主要代码。。
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
int n,m;
int dis[103],a[103],b[103],s[103];
while(~scanf(“%d%d”,&n,&m))
{
for(int i=1;i<=n;i++)
dis[i]=0x1f1f1f;
dis[1]=0;
for(int i=1;i<=n;i++)
{
scanf(“%d%d%d”,&a[i],&b[i],&s[i]);
}
for(int k=1;k<n-1;k++)
{
for(int i=1;i<=m;i++)
{
if(dis[b[i]]>dis[a[i]]+s[i])
{
dis[b[i]]=dis[a[i]]+s[i];
}
}
}
for(int i=1;i<=n;i++)
{
printf(“%d “,dis[i]);
}
}
}
/*
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/
神奇的树
一.堆排序
找位置
从根结点开始向下调整
void sift_down(int i)
{
int t,flag=0;
while(i*2<=n&&flag==0)//如果存在子节点那么i*2<=n
{
if(h[i]>h[i*2])
{
t=i*2;
}
else t=i;
if(i*2+1<=n&&h[t]>h[i*2+1])
{
t=i*2+1;
}
if(t!=i)
{
swap_h(t,i);
i=t;
}
//flag=1,t==i.因为她的子节点都比它大所以不需要再往下遍历了
else flag=1;
}
return;
}
建立树的模型
void creat()
{
//从第一个非叶节点到第一个节点一次向上进行调整
//节点分为三类。根节点。叶结点,内部结点
for(int i=n/2;i>=1;i–)
{
sift_down(i);
}
return;
}
当结点交换时所用
void swap_h(int a,int b)
{
int t=h[a];
h[a]=h[b];
h[b]=t;
return;
}
堆排序样例码
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
int n,h[100003];
//当确定子节点的位置的时候我们就需要进行交换两个位置节点的数值
void swap_h(int a,int b)
{
int t=h[a];
h[a]=h[b];
h[b]=t;
return;
}
void sift_down(int i)
{
int t,flag=0;
while(i*2<=n&&flag==0)//如果存在子节点那么i*2<=n
{
if(h[i]>h[i*2])
{
t=i*2;
}
else t=i;
if(i*2+1<=n&&h[t]>h[i*2+1])
{
t=i*2+1;
}
if(t!=i)
{
swap_h(t,i);
i=t;
}
//flag=1,t==i.因为她的子节点都比它大所以不需要再往下遍历了
else flag=1;
}
return;
}
//建立树的模型
void creat()
{
for(int i=n/2;i>=1;i–)
{
sift_down(i);
}
return;
}
int delete_min()
{
int t=h[1];
h[1]=h[n];
n–;
sift_down(1);
return t;
}
int main()
{
int i,num;
while(~scanf(“%d”,&num))
{
for(int i=1;i<=num;i++)
{
scanf(“%d”,&h[i]);
}
n=num;
creat();
for(int i=1;i<=num;i++)
{
printf(“%d “,delete_min());
}
printf(“\n”);
}
}
/*
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
*/