最少转机
p142
就是求权值为1的无向图 从一个点到另一个点最短路径
我写了两种做法
还有3种做法后面补充
- dfs版
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,startx,endx;
int a,b,minn=99999999;
int e[1000][1000],vis[1000];
void dfs(int cur,int sum)//cur代表正在访问点的编号 sum表示路径总和
{
if(sum>minn) return ;//当路径大于最优解 (剪枝)
if(cur==endx){//到达终点
minn=min(minn,sum);//更新最小值
return ;
}
for(int i=1;i<=n;i++){
if(e[cur][i]==1&&vis[i]==0)//判断
{
vis[i]=1;//标记
dfs(i,sum+1);//继续深搜
vis[i]=0;//回溯
//因为有多种走法 所以要回溯 为下一次
}
}
}
int main()
{
cin>>n>>m>>startx>>endx;//输入
//邻接矩阵存图
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) e[i][j]==0;//自己到自己为路径为0
else e[i][j]=minn;//其他赋值为正无穷 就是无法走
for(int i=1;i<=m;i++)
{
cin>>a>>b;//输入两端点的路
e[a][b]=e[b][a]=1;//无向图
}
vis[startx]=1;//一定要标记 因为是从startx开始的
dfs(startx,0);
cout<<minn<<endl;//最后输出
return 0;
}
- bfs版
队列模拟废话
本蒟蒻还不会stl 只好手写
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct x{
int sum,node;//sum存路径总和 node表示正在访问点的编号
}q[100000];
int n,m,startx,endx;
int a,b,minn=99999999;
int e[1000][1000],vis[1000];
int head,tail;
void init()
{
cin>>n>>m>>startx>>endx;
//邻接矩存图
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) e[i][j]=0;//不重负说
else e[i][j]=minn;
for(int i=1;i<=m;i++)
{
cin>>a>>b;
e[a][b]=e[b][a]=1;
//再次强调这是无向图
}
}
int main()
{
int flag=0;
init();
head=1,tail=1;//初始化队列为空
q[tail].node=startx;//第一个元素入队
q[tail].sum=0;//刚开始为0
vis[startx]=1;//标记已经入队元素
tail++;//更新尾指针
while(head<tail)//当队列不为空
{
int cur=q[head].node;//当前队列的首城市编号
for(int i=1;i<=n;i++)
{
if(e[cur][i]==1&&vis[i]==0)//判断
{
q[tail].node=i;//更新入队
vis[i]=1;//标记
q[tail].sum=q[head].sum+1;//为首城市转机次数+1
tail++;//更新尾指针
}
if(q[tail-1].node==endx)//当到达终点
//一定要注意 tail是一直指向下一个元素的 所以要-1
{
flag=1;
break;
}
}
if(flag) break;
head++;//别忘了出队了
}
cout<<q[tail-1].sum<<endl;
//注意tail是指向队尾元素的下一个 千万记住 所以-1
return 0;
}
这里一定要注意一点 只有当每条边权值相等时 才可以用bfs
如题中 每条边权值为1
如果权值不相等 bfs就不能保证在扩展时得到最优解 就不正确了
- Floyd
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,sx,ex,e[1000][1000],minn=9999999,a,b;
int main()
{
//邻接矩阵存图法
//这个非常重要 到时候dlj也要用到 前面dfs bfs也用到了
scanf("%d %d %d %d",&n,&m,&sx,&ex);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) e[i][j]=0;
else e[i][j]=minn;
}
}
for(int i=1;i<=m;i++)
{
cin>>a>>b;
e[a][b]=e[b][a]=1;
}
//floyd核心代码
for(int k=1;k<=n;k++)
{
for(int i=1;i<+n;i++)
{
for(int j=1;j<=n;j++)
{
if(e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j];//dp过程
//这个的思想是 枚举一个中间点 依次带入(i,j)去试
//然后慢慢更新 时间复杂度是n^3
}
}
}
cout<<e[sx][ex]<<endl;
return 0;
}```
dijkstra做法
#include
#include
using namespace std;
int n,m,e[1000][1000],dis[1000],a,b,vis[10000],u,minn,inf=9999999;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) e[i][j]=0;
else e[i][j]=inf;
for(int i=1;i<=m;i++)
{
cin>>a>>b;
e[a][b]=e[b][a]=1;
}
for(int i=1;i<=n;i++) dis[i]=e[1][i];
vis[1]=1;
for(int i=1;i<=n-1;i++)
{
minn=inf;
//更新最小值
for(int j=2;j<=n;j++)
{
if(vis[j]==0&&dis[j]<minn){minn=dis[j];u=j;}
}
vis[u]=1;
//核心代码
for(int v=1;v<=n;v++)
{
dis[v]=min(dis[v],dis[u]+e[u][v]);
}
}
cout<<dis[n]<<endl;
return 0;
}