IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 2021年暑训最短路专题题解(待补全) -> 正文阅读

[数据结构与算法]2021年暑训最短路专题题解(待补全)

昨天一天才把DJ&SPFA卷明白,麻了麻了

2021年暑训最短路专题

Problem A:

Til the Cows Come Home
题目大意:已知一个有T条边,N个点的无向图,求从点1到点N的最短路(板子题)
题解:数据量不大,DJ可以不需要优化,当点的数据超过1e4时最好采用堆优化DJ。
AC代码:(堆优化DJ)

#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=2e3+7;
const int inf=0x3f3f3f3f;
struct edge{
    int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
int n,t;
priority_queue<P,vector<P>,greater<P> > que;
void dij(int s){
    fill(d+1,d+1+n,inf);
    d[s]=0;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost) {
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to],e.to));
            }
        }
    }
}
int main(){
    cin>>t>>n;
    for(int i=1;i<=t;i++){
        int a,b,c;
        cin>>a>>b>>c;
        edge e;
        e.to=b,e.cost=c;
        G[a].push_back(e);
        e.to=a;
        G[b].push_back(e);
    }
    dij(1);
    cout<<d[n];
    return 0;
}

Problem B:

Frogger
题目大意:青蛙A在第一块石头,青蛙B在第二块石头,旁边还有其他石头,给出每块石头的坐标,青蛙A有很多种方案到达第二块石头,它想知道所有路径中最大的最短距离。
题解:(堆优化DJ)这道题难在无法理解如何松弛,在求最短路的时候,d[x]数组保存的是从源点到达点x路径中最大的最短距离,由于是求最短距离,
我们的堆要是小顶堆,这样才能保证每次取出的路径是最短的,在此基础上对每个端点进行松弛(d[i]=min(d[i],max(d[v],e.cost))),尽量让最短的边达到最大。
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<math.h>
#include<iomanip>
using namespace std;
typedef pair<int,double> P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
   int to;
   double cost;
};
vector<edge>G[maxn];
int n;
double x[202],y[202];//石块坐标
double d[maxn];
void dij(int s){
    fill(d+1,d+1+n,inf);
    d[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;//小根堆
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>max(d[v],e.cost)){//松弛操作//针对于inf,将d[x]=inf转化为max(d[v],e.cost)
                d[e.to]=max(d[v],e.cost);
                que.push(P(d[e.to],e.to));//处理完所有点后得到的d[n]就是我们要求的最大的最小值
            }
        }
    }
}
int main(){
    int cnt=1;
    while(cin>>n&&n){
        for(int i=1;i<=202;i++)G[i].clear();
        for(int i=1;i<=n;i++){
            cin>>x[i]>>y[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                double dis=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
                edge e;
                e.to=j;
                e.cost=dis;
                G[i].push_back(e);
            }
        }//邻接表存图
        dij(1);
        cout<<"Scenario #"<<cnt++<<endl<<"Frog Distance = ";
        cout<<fixed<<setprecision(3)<<d[2]<<endl<<endl;
    }
}

Problem C:

Heavy Transportation
题目大意:与B类似,求所有路径中最小的最大值。
题解:与B类似,d[x]数组初始化为0,d[s]初始化为inf,优先队列选用大根堆,松弛方程为d[e.to]=max(d[e.to],min(d[v],e.cost));
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<stdio.h>
using namespace std;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
typedef pair<int,int>P;
int t,n,m;
struct edge {
    int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
void dj(int s){
    fill(d+1,d+1+n,0);
    d[s]=inf;
    priority_queue<P,vector<P>,less<P> >que;
    que.push(P(inf,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]<min(d[v],e.cost)){
                d[e.to]=min(d[v],e.cost);
                que.push(P(d[e.to],e.to));
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    int cnt=1;
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            edge e;
            e.to=a,e.cost=c;
            G[b].push_back(e);
            e.to=b;
            G[a].push_back(e);
        }
        dj(1);
        printf("Scenario #%d:\n%d\n\n",cnt++,d[n]);
        for(int i=1;i<=n;i++)G[i].clear();
    }
    return 0;
}

Problem D:

Silver Cow Party
题目大意:N个农场,M条路(单向边),其中编号为X的农场要举行party,求其他农场的cow们中往返时间最长的那只的往返时间。
题解:自己画张图很容易证明a到b的最短路,可以由b到a所求最短路的矩阵的转置所求出。所以本题两次Dj即可完成。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
    int to,cost;
};
vector<edge>G1[maxn],G2[maxn];//两张图,G2是G1的转置
int n,m,x,d1[maxn],d2[maxn];//d1和d2分别是G1和G2图中所求最短路
void dj1(int s){//G1的最短路
    fill(d1+1,d1+1+n,inf);
    d1[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d1[v]<p.first)continue;
        for(int i=0;i<G1[v].size();i++){
            edge e=G1[v][i];
            if(d1[e.to]>d1[v]+e.cost){
                d1[e.to]=d1[v]+e.cost;
                que.push(P(d1[e.to],e.to));
            }
        }
    }
}
void dj2(int s){//G2的最短路
    fill(d2+1,d2+1+n,inf);
    d2[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d2[v]<p.first)continue;
        for(int i=0;i<G2[v].size();i++){
            edge e=G2[v][i];
            if(d2[e.to]>d2[v]+e.cost){
                d2[e.to]=d2[v]+e.cost;
                que.push(P(d2[e.to],e.to));
            }
        }
    }
}
int main(){
    cin>>n>>m>>x;
    for(int i=1;i<=m;i++){//邻接表存图
        int a,b,c;
        cin>>a>>b>>c;
        edge e;
        e.to=b,e.cost=c;
        G1[a].push_back(e);
        e.to=a;
        G2[b].push_back(e);
    }
    dj1(x);
    dj2(x);
    int ans=0;
    for(int i=1;i<=n;i++){//找出所有cow中花费时间最长的那只
        ans=max(ans,d1[i]+d2[i]);
    }
    cout<<ans;
}

Problem E

Currency Exchange
题目大意:已知你是Nike,你家附近有M台货币转换机,可以转换N种货币,你目前有S种类的货币V枚,每台货币转换机具有六个数据:a,b为两种货币的类型,Cab、Rab是a转化为b的汇率及佣金,Cba、Rba是b转化为a的汇率及佣金。
a转化为b的转化公式:设a有k枚,那么可以转化(k-Rab)*Cab枚b。
问:是否有可能在转化(不限次数)后,使自己本金变多?
题解:由于需要增加本金,那么我们需要在这些转换规则中找到一个正环,
利用SPFA解决问题。
AC代码

#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e7;
const int M = 1e5+7;
#define INF 1e7+5
#define INM INT_MIN
#define pb(a)  push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define sdd(a,b) scanf("%d %d",&a,&b)
#define sddd(a,b,c) scanf("%d %d %d",&a,&b,&c)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
#define pr_(a) printf("%d ",a)
#define _pr(a) printf(" %d",a)
int vis[105],n,m,s;
double dis[105],v;
struct Node
{
    int to;
    double a1,b1;//存转换规则,只存A-B的,因为看成有向边
};
vector<Node> G[105];
bool spfa(int x)
{
    memset(vis,0,sizeof(vis));
    vis[x] = 1;
    for(int i=1;i<=n;++i) dis[i] = 0;//这里应该是找最大路
    dis[x] = v;
    queue<int> Q;
    Q.push(x);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;//消除标记
        for(int i=0;i<G[u].size();++i)
        {
            Node e = G[u][i];
            if(dis[e.to] < (dis[u]-e.b1)*e.a1)
            {
                dis[e.to] = (dis[u]-e.b1)*e.a1;
                if(!vis[e.to])
                {
                    vis[e.to] = 1;//添加标记
                    Q.push(e.to);
                }
            }
            if(dis[x] > v) return true;//判断是否存在正环了
        }
    }
    return false;
}
int main()
{
    sddd(n,m,s);
    scanf("%lf",&v);
    while(m--)
    {
        int a,b;
        double a1,b1,a2,b2;
        sdd(a,b);
        scanf("%lf %lf %lf %lf",&a1,&b1,&a2,&b2);
        Node p,q;
        p.to = b;p.a1 = a1;p.b1 = b1;
        q.to = a;q.a1 = a2;q.b1 = b2;
        G[a].pb(p);
        G[b].pb(q);
    }
    if(spfa(s)) printf("YES\n");
    else printf("NO\n");
}

Problem F:

Wormholes
题目大意:一个农夫有N块田,M条路(双向),每条路需要花费一定时间,他的田中还存在W个虫洞,这些虫洞可以将农夫传送回一定时间以前(单向),问:有没有一种方案能使他回到初始点与自己碰面。
题解:虫洞的时间为负,SPFA判断是否存在负环即可。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;//first为点,second为记录迭代了多少次
const int maxn=5e2+7;
const int inf=0x3f3f3f3f;
int d[maxn];
int n,m,w;
int vis[maxn];
struct edge{
    int to,cost;
};
vector<edge>G[maxn];
bool SPFA(int s){
    int cnt[maxn]={0};
    fill(d+1,d+1+n,inf);
    fill(vis+1,vis+1+n,0);
    d[s]=0;
    vis[s]=1;
    queue<P>que;
    que.push(P(s,cnt[s]));
    while(!que.empty()){
        P p=que.front();
        que.pop();
        int v=p.first;
        vis[v]=0;//点出队,成环时用得到
        //if(p.second>n)return 0;
        for(int i=0;i<G[v].size();i++){//判断与V相邻的点
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost){//松弛
                d[e.to]=d[v]+e.cost;
                if(!vis[e.to]){
                    que.push(P(e.to,++cnt[e.to]));
                    vis[e.to]=1;
                }
            }
        }
        for(int i=1;i<=n;i++){
            if(cnt[i]>n)return 0;
        }
    }
    return 1;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        for(int i=1;i<=maxn;i++)G[i].clear();
        cin>>n>>m>>w;
        for(int i=1;i<=m;i++){
            int a,b,c;
            edge e;
            cin>>a>>b>>c;
            e.to=b,e.cost=c;
            G[a].push_back(e);
            e.to=a;
            G[b].push_back(e);
        }
        for(int i=1;i<=w;i++){
            int a,b,c;
            edge e;
            cin>>a>>b>>c;
            e.to=b,e.cost=-c;
            G[a].push_back(e);
        }
        if(SPFA(1)){
            cout<<"NO"<<endl;
        }
        else cout<<"YES"<<endl;
    }
    return 0;
}

待补全

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:33:17  更:2021-07-16 11:34:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:51:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码