寒假训练营 第十二节 搜索与图论(三)总结

本文介绍了Dijkstra算法和Floyd算法在解决图中求最短路径问题的应用。Dijkstra算法适用于单源最短路径,不处理负权边,而Floyd算法能处理多源最短路径,包括负权边但不能有负权环。文章通过示例题目解释了两种算法的基本原理和特点,并提供了相应的C++实现代码片段。
摘要由CSDN通过智能技术生成

迪杰斯特拉(Dijkstra,单源最短路)

Dijkstra算法图文详解
基本原理

从起始点出发,重复寻找当前距离起始点最近的且未访问过的结点,然后利用该结点更新距离数组,直到访问过全部结点为止,最终的距离数组即为起始点到其余个点的最短路径距离。

特点

它不允许边有负权值,由于在第一步时就会将与源点最近的点确认为最短,如果这个是可以被缩短的那这个确认是就是错误的。

void dijkstra(int s)
    {
        memset(dis,MM,sizeof(dis));
        dis[s] = 0;
        for (int j = 1; j <= n; j++)
        {
            minn = MM;
            vis[s] = 1;
            for (int i = 1; i <= n; i++)
            {
                if (dis[i] < minn && vis[i] == 0)
                {
                    minn = dis[i];
                    s = i;
                }
            }

            for (int i = first_edge[s]; i != 0; i = edge[i].next)
            {
                if (vis[edge[i].to] == 0 && dis[s] + edge[i].value < dis[edge[i].to])
                {
                    dis[edge[i].to] = dis[s] + edge[i].value;
                    pre[edge[i].to] =s;
                }
            }
        }
    }
1、题目详情:洛谷P1576 最小花费

n n n 个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A A A 最少需要多少钱使得转账后 B B B 收到 100 100 100 元。

输入格式

第一行输入两个正整数 n , m n,m n,m,分别表示总人数和可以互相转账的人的对数。以下 m m m 行每行输入三个正整数 x , y , z x,y,z x,y,z,表示标号为 x x x 的人和标号为 y y y 的人之间互相转账需要扣除 z % z\% z% 的手续费 ( z < 100 ) (z<100) (z<100)
最后一行输入两个正整数 A , B A,B A,B。数据保证 A A A B B B 之间可以直接或间接地转账。

输出格式

输出 A A A 使得 B B B 到账 100 100 100 元最少需要的总费用。精确到小数点后 8 8 8 位。

提示

1 ≤ n ≤ 2000 , m ≤ 100000 1\le n \le 2000,m\le 100000 1n2000,m100000

#include<bits/stdc++.h>
using namespace std;
double a[2222][2222],dis[2222]={0},minn;
int n,m,i,j,k,x,y;
int f[2222]={0};
void dijkstra(int x){
	for(int i=1;i<=n;i++)
		dis[i]=a[x][i];
	dis[x]=1;
	f[x]=1;
	for(int i=1;i<=n-1;i++){
		minn=0;
		for(int j=1;j<=n;j++)
			if(f[j]==0 && dis[j]>minn){
				k=j;
				minn=dis[j];
			}
		f[k]=1;
		if(k==y)
			break;
		for(int j=1;j<=n;j++)
			if(f[j]==0 && dis[k]*a[k][j]>dis[j])
			dis[j]=dis[k]*a[k][j];
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&j,&k);
		scanf("%lf",&a[j][k]);
		a[j][k]=(100-a[j][k])/100;
		a[k][j]=a[j][k];
	}
	cin>>x>>y;
	dijkstra(x);
	printf("%0.8lf",100/dis[y]);
	return 0;
}

弗洛伊德(Floyd,多源最短路)

弗洛伊德(Floyd)算法详解
基本原理

选取某个节点k作为i到j需要经过的中间节点,通过比较d(i,k)+d(k,j)和现有d(i,j)的大小,将较小值更新为路径长度,对k节点的选取进行遍历,以得到在经过所有节点时i到j的最短路径长度,通过不断加入中间点的方式更新最短路径。这个算法代码十分简洁,优雅,背后的思想是基于动态规划。

特点

边的权值正值负值均可,但是不可处理有负权环的情况。

    for (int k = 1; k <= n; k++)
    {
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (a[i][j] > a[i][k] + a[k][j])
                {
                    a[i][j] = a[i][k] + a[k][j];
                    p[i][j] = k;
                }
            }
        }
    }
2、题目详情:洛谷P1346 电车

在一个神奇的小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能)。在每个路口,都有一个开关决定着出去的轨道,每个开关都有一个默认的状态,每辆电车行驶到路口之后,只能从开关所指向的轨道出去,如果电车司机想走另一个轨道,他就必须下车切换开关的状态。
为了行驶向目标地点,电车司机不得不经常下车来切换开关,于是,他们想请你写一个程序,计算一辆从路口 A A A 到路口 B B B 最少需要下车切换几次开关。

输入格式

第一行有 3 3 3 个整数 N , A , B N,A,B N,A,B 2 ≤ N ≤ 100 , 1 ≤ A , B ≤ N 2 \leq N \leq 100, 1 \leq A,B \leq N 2N100,1A,BN),分别表示路口的数量,和电车的起点,终点。
接下来有 N N N 行,每行的开头有一个数字 K i K_i Ki 0 ≤ K i ≤ N − 1 0 \leq K_i \leq N-1 0KiN1),表示这个路口与 K i K_i Ki 条轨道相连,接下来有 K i K_i Ki 个数字表示每条轨道所通向的路口,开关默认指向第一个数字表示的轨道。

输出格式

输出文件只有一个数字,表示从 A A A B B B 所需的最少的切换开关次数,若无法从 A A A 前往 B B B,输出 − 1 -1 1

#include<bits/stdc++.h>
using namespace std;
int main(){
    int i,k,j,num,a[110][110],n,from,end,b[110][110];;
    cin>>n>>from>>end;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            a[i][j]=99999; 
    for(i=1;i<=n;i++){//读入
        scanf("%d",&num);
        for(j=1;j<=num;j++){
            scanf("%d",&k);
            if(j==1) 
            	a[i][k]=0;
            else 
            	a[i][k]=1;
            b[i][k]=1;
        }
    }
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(b[i][k]==1&&b[k][j]==1) 
                	a[i][j]=min(a[i][k]+a[k][j],a[i][j]),b[i][j]=1;
    if(b[from][end]==1)
    	cout<<a[from][end];
    else 
    	cout<<"-1";
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值