南华大学第十五届ACM程序设计竞赛(重现赛)E 免费机票(单边最短路)

题目链接

题目描述

        某华大学小飞中奖了!!!奖品是一张免费飞机票,唯一遗憾的是,这张飞机票有限定区间,需要从k个区间中选择其一。小飞打算高高兴兴的出去玩啦,但是,从s地出发,去往e地,可能没有直达的飞机票,可能需要转机(所有飞机线路都是无向的),小飞毕竟是个学生党,出去玩首先得考虑省钱,所以,小飞遇到麻烦了,请帮小飞计算最便宜的一条路线,小飞会很感激你的。

输入描述:

第一行为三个整数n,s,e,n表示n个不同城市的飞机场,s为出发点,e为目的地。(1<=n<=1000,  1<=s,e<=n)

第二行为一个整数m,表示m条普通飞机线路,接下来的m行描述每条线路,每行包含三个整数a、b、c,a、b代表普通飞机线路两端,c表示价格。(1<=m<=1000,  1<=a,b<=n,  1<=c<=1000)

接下来的一行为一个整数k,表示k个免费机票的航线区间, 然后k行来描述每条免费航线,每行两个整数a’、b’,    分别代表免费航线两端。(1<=k<=1000,  1<=a’,b’<=n)

输出描述:

每个测试数据有两行输出,第一行为是否使用免费飞机票,是则输出“Yes”,否则输出“No”。第二行输出总共花费。

示例1

输入

4 1 4
3
1 2 1
1 3 2
2 4 3
1
3 4

输出

Yes
2

示例2

输入

7 1 7
5
1 4 1
1 5 2
1 7 10
4 7 8
5 7 5
2
2 4
6 7

输出

No
7

题意:就是一个有n个点的图,里面有m条边,现在可以把给出的K条边其中的一条路径话费变成0,现在要求在这种条件下从s到e的最小话费,并且判断最小花费是否走了k条路中的一条。.

题解:其实题理解很简单,就是一个最短路,但是如果一个一个判断k条路那条赋值为0,话费最优的话,肯定会超时。所以这里就要求单边最短路。首先那两个数组记录起点与终点到每个点的最小花费。然后就去遍历k条路的每一条,例如比如说(u,v)之间的权值可赋值为0,就比较开始跑的最短路s到e的花费和min(dis1[u]+dis2[v],dis1[v]+dis2[u]);的花费那个小,(这个式子表示s到u的花费+e到v的花费,正好减去了u,v的花费,取min很容易理解)。然后这样遍历每一条边,要是新的花费比原来的小,说明走这条路更优,一直取最小值就是答案,细节看代码。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
//#include<unordered_map>
//#include<unordered_set>
const int maxn=1e3+10;
const int mod=20190414;
const int inf=1e9;
const long long onf=1e18;
#define me(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define mid l+(r-l)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI 3.14159265358979323846
int dir[4][2]= {0,-1,-1,0,0,1,1,0};
int dx[]= {-2,-2,-1,-1,1,1,2,2};
int dy[]= {-1,1,-2,2,-2,2,-1,1};
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int maps[maxn][maxn];
int dis1[maxn],dis2[maxn];
int n;
void DIJ(int s,int *dis)
{
    bool vis[maxn];me(vis,0);
    fill(dis,dis+maxn,inf);
    dis[s]=0;
    for(int i=1; i<=n; i++)
    {
        int Min=inf,u=-1;
        for(int j=1; j<=n; j++)
            if(!vis[j]&&(u==-1||Min>dis[j]))
                u=j,Min=dis[j];
        if(u==-1)
            return ;
        vis[u]=1;
        for(int j=1; j<=n; j++)
            if(!vis[j]&&maps[u][j]+dis[u]<dis[j])
                dis[j]=dis[u]+maps[u][j];
    }
}
int main()
{
    int s,e,m;
    scanf("%d%d%d",&n,&s,&e);
    scanf("%d",&m);
    fill(maps[0],maps[0]+maxn*maxn,inf);
    for(int i=1;i<=n;i++)
        maps[i][i]=0;
    for(int i=0; i<m; i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        maps[u][v]=maps[v][u]=w;
    }
    int k;
    bool flag=0;
    DIJ(s,dis1),DIJ(e,dis2);///dis1保存s到每点的最短距离,dis2保存e到每点最短距离。
    int Min=dis1[e];///保存最初的花费,方便后面做比较
    scanf("%d",&k);
    for(int i=0; i<k; i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        int temp=min(dis1[u]+dis2[v],dis1[v]+dis2[u]);///比较当前路权值为0时,答案会不会更优
        if(temp<Min)///一直找最优解
        {
            flag=1;
            Min=temp;
        }
    }
    if(flag)
        puts("Yes");
    else
        puts("No");
    printf("%d\n",Min);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值