kuangbin 最短路刷题 (1) ABC

本文介绍了如何运用Dijkstra算法处理单调问题,如求解单调增路径中的最长边,以及在Floyd-Warshall和最小生成树背景下,通过调整判断条件来求解1-N的最短路径中最大承重问题。通过实例演示了如何在不同场景下应用这一经典算法。
摘要由CSDN通过智能技术生成

目前已完成
ABC

小总结: 如果一个问题是单调的,就可以用dij的思想

首先,更广泛的去明白“单调”——即不变的,单一的性质

譬如,单调增的性质(体现在数上面,随着i向前推进): a i < a i+1

A:
dij模板题

using namespace std;
const int N=2100,inf=0x3f3f3f3f;

struct Edge{
    int u,v,w;
};

vector <Edge> eg[N];
int d[N];
bool st[N];

void dij(){
    
	memset(d,inf,sizeof d);//ms(v,0);
   	priority_queue<PII,vector<PII>,greater<PII> >pq;
    pq.push({0,1});
    d[1]=0;
    while(!pq.empty()){

        PII now = pq.top();
        pq.pop();

        int u = now.second;

        if(st[u])
            continue;
        st[u] = 1;

        for (int i = 0; i < eg[u].size();i++)
        {
			int v = eg[u][i].v, w = eg[u][i].w;

            if(d[v]>d[u]+w){
                d[v] = d[u] + w;
                //pre[v]=u;
                pq.push({d[v], v});
            }
		}
	}
    
}

int main(){
	int t,n;cin>>t>>n;
	For(i,1,t){
		int u,v,w;cin>>u>>v>>w;
		eg[u].push_back({u,v,w});
        eg[v].push_back({v,u,w});
	}
	
	dij();
	cout<<d[n]<<endl;
    return 0;
}

B:
求1-2的可行路径中,找到每条路最长的那条边,再取得所有路得最长边中的最短那个边就是

floyd:
三个for循环不变,k重…允许从1…到k个结点…递推
f函数变化,f[ i ][ j ] 表示 从i-j这条路上的最大值的最小值
f [ j ] [i] = f[i][j] = min(f[i][j], max(f[i][k] , f[k][j]));

dij: 维护d[ N ],为 1-x 路线的 答案(最小的最长边)
(假定被更新对象为d[ k ] ,那么只有用 d[ i ] (d[ i ] < d[ k ] ) 才可能更新 )

const int N=210,inf=0x3f3f3f3f;

struct po{
    int x, y;
} p[N];

double dis(po a, po b) {return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));}

double f[N][N];

int main(){
    int k = 0;
    while(1){
        k++;
        int n;
        cin >> n;
        
        if(n==0)break;

        ms(p, 0);
        ms(f, 0);
        _for(i,0,n){
            cin >> p[i].x >> p[i].y;
        }

        _for(i, 0, n) _for(j, 0, n) f[j][i]=f[i][j] = dis(p[i], p[j]);

        _for(k,0,n)_for(i, 0, n) _for(j, 0, n){
            f[j][i] = f[i][j] = min(f[i][j], max(f[i][k] , f[k][j]));
           
        }

        printf("Scenario #%d\nFrog Distance = %.3f\n\n",k,f[0][1]);
    }
    cout << endl;
    return 0;
}

dij:类似与A题,判断条件改一下

for (int i = 0; i < eg[u].size();i++)
        {
			int v = eg[u][i].v;double w = eg[u][i].w;
			
			double Min=max(d[u],w);
			
            if(d[v]>Min){
                d[v] = Min;
                //pre[v]=u;
                pq.push({d[v], v});
            }
		}

C题 与B题类似
求的是
最小的那条边(因为一条路中的允许重量取决于最小值),再把每条路中最小的这条边中取最大的
用dij: 思路 : 为了确定 1-n最短路 先确定 1 - i1 的最短路 , 在确定 1-i2…
一直确定到 1-n…

主要是维护d 数组
最短路里代表从起始点到达当前点的最短路径
(prim算法里的则代表把 当前点连入 到 已经连好的部分图形 中的最小距离)
这个题中数组则 代表从 起始点 到达当前点的 最大承重

#include <iostream>
#include <cstring>
#include <string>
#include<cstdio>
#include<map>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
#define PII pair<int, int>
#define PDI pair<double,int>
#define _for(i, a, b) for (int i = a; i < b; i++)
#define For(i, a, b) for (int i = a; i <= b; i++)
#define foR(i, b, a) for (int i = b; i >= a; i--)
#define ms(a,b) memset(a, b, sizeof a)
#define gcd(a, b) __gcd(a, b)
#define lcm(a, b) a / gcd(a, b) * b
#define plf(a) printf("%lf\n",a)
#define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
using namespace std;
const int N=2100,inf=0x3f3f3f3f;

struct Edge{
    int u,v,w;
};

vector <Edge> eg[N];
int d[N];
bool st[N];

void dij(){
    
	ms(d,0);
	ms(st,0);
   	priority_queue<PII>pq;
    pq.push({inf,1});
    d[1]=inf;
    
    while(!pq.empty()){

        PII now = pq.top();
        pq.pop();

        int u = now.second;

        if(st[u])
            continue;
        st[u] = 1;

        for (int i = 0; i < eg[u].size();i++)
        {
			int v = eg[u][i].v, w = eg[u][i].w;
			
			int Max=min(d[u],w);
			
            if(d[v]<Max){
                d[v] = Max;
                //pre[v]=u;
                pq.push({d[v], v});
            }
		}
	}
    
}
int main(){	
	int t;cin>>t;
    For(k,1,t){
        int n,m;
		cin>>n>>m; 

    	ms(eg,0);
    	
    	For(i,1,m){
            int u,v,w;sc3(u,v,w);
            eg[u].push_back({u,v,w});
       		eg[v].push_back({v,u,w});
        }
		
		
		 
		dij();

        printf("Scenario #%d:\n%d\n\n",k,d[n]);
    }
    return 0;
}

用最小生成树去写:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值