牛客练习赛61—最短路变短了

题目链接

题目描述
给定一个有向带权图,其中包括 n个城市,城市编号从1 到n,有向边的编号从 1 到 m,现在有一个人位于城市 1,并且想要到城市n旅游。现在政府正在决定将一条道路反向,这个人想知道在某一指定道路反向的情况到达城市n最短路径长度会不会变短。 保证开始给定的图从城市1可以到达城市n,若边反向后城市1不能到达城市n,我们视为最短路径长度没有变短。

输入描述:
第一行包括两个整数n,m(1≤n≤100000,1≤m≤200000)

接下来m行每行三个整数u,v,c (1≤u,v≤n, 1≤c≤10^9),分别代表一条有向边的起点,终点和边权。保证没有自环。

接下来一行一个整数q(1≤q≤200000),代表查询组数,查询之间是独立的。

接下来q行每行一个整数x(1≤x≤m),代表询问将第x条边反向后到达城市n的最短路长度会不会变短。

输出描述:
共q行,如果最短路变短输出YES,否则输出NO。

题解:其实这题最简单的方法就是Floyd算法,只不过时间会超,其实这里也不难想到用Dijkstra算法,因为常用的求最短路里只有它不会超时。思路如下
此图来自dmu学长的ppt
此图来自学长的ppt
我们假设我们要修改3和6之间的方向
在这里插入图片描述
既然修改了这条边,如果不从这条边走,修改就毫无意义(因为对最短的距离没有影响),故我们必定要走这条路,因为修改前方向是6-3,故修改后是3-6,这里我们假设从s点(起点)到3的最短距离为d【3】,终点t到6的最短距离为b【6】,则修改后且必须走这条路从起点到终点的最短距离为b【3】+d【6】+num(这条路的长度),相信有的同学已经知道怎么做了,我们需要以s点为起点用Dijkstra算法求到所有点的最短路,再以t点为起点求到所有点的最短路,在按照上面的公示,答案就出来了。
代码如下

c++代码

#include<iostream>
#include<queue>
#include<string.h>
#include<string>
#include<vector>
#include<map>
typedef long long ll;
using namespace std;

 
#define ll long long
#define maxn 100005
#define INF 2147483647000000000
#define IOS ios::sync_with_stdio(false)

struct edge
{
    ll to, cost;
};
typedef pair<ll, ll> P;
ll n, m, st;
ll d[maxn],d0[maxn],d1[maxn];
bool visited[maxn];
 
vector<edge> G0[maxn],G1[maxn];
 
void dijsktra(ll s, ll V,vector<edge>G[maxn])
{
    priority_queue<P, vector<P>, greater<P> > que;
    for (ll i = 0; i < V; i++) d[i] = INF;
    memset(visited, 0, sizeof(visited));
    d[s] = 0;
    que.push(P(0, s));
    while (!que.empty())
    {
        P p = que.top();
        que.pop();
        ll v = p.second;
        if (d[v] < p.first) continue;
        for (ll 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));
            }
        }
    }
}
 
struct node{
    ll u,v,dis;
};
map<ll,node>mp;
 
int main()
{
    IOS;
    ll n,m;
    cin>>n>>m;
 
    for(int i=0;i<m;i++)
    {
        ll u,v,dis;
        cin>>u>>v>>dis;
        G0[u].push_back(edge{v,dis});
        G1[v].push_back(edge{u,dis});//反向图
        mp[i+1]=node{u,v,dis};
    }
 
    dijsktra(1,n+1,G0);
    for(int i=1;i<=n;i++)
        d0[i]=d[i];
    dijsktra(n,n+1,G1);
    for(int i=1;i<=n;i++)
        d1[i]=d[i];
 
    ll Q;
    cin>>Q;
    while(Q--)
    {
        ll id;
        cin>>id;
        node t0=mp[id];
        ll dis1=d0[t0.v],dis2=d1[t0.u];
        if(d0[n]>dis1+dis2+t0.dis)
            cout<<"YES\n";
        else
            cout<<"NO\n";
    }
    return 0;
}

java代码

import java.io.*;
public class Main {
    static int na,nb,nc,nd,m,ans;
    static char[] select=new char[13];
    static int arr[][];
     
    static void dfs(int index,int a,int b,int c,int d) {
        if(a>na||b>nb||c>nc||d>nd) return ;
         
        for(int i=0;i<m;i++) {
            int x=arr[i][0];
            int y=arr[i][1];
            if(index>x&&index>y&&select[x]!=select[y]) return;
        }
        if(index==13) {
            if(a==na&&b==nb&&c==nc&&d==nd) {
                ans++;
            }
            return ;
        }
 
        select[index]='a';
        dfs(index+1,a+1,b,c,d);
        select[index]='b';
        dfs(index+1,a,b+1,c,d);
        select[index]='c';
        dfs(index+1,a,b,c+1,d);
        select[index]='d';
        dfs(index+1,a,b,c,d+1);
         
    }
 
    public static void main(String[] args) throws IOException {
        StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        st.nextToken();
        na=(int)st.nval;
        st.nextToken();
        nb=(int)st.nval;
        st.nextToken();
        nc=(int)st.nval;
        st.nextToken();
        nd=(int)st.nval;
        st.nextToken();
        m=(int)st.nval;
        arr=new int[m][2];
        for(int i=0;i<m;i++) {
            for(int j=0;j<2;j++) {
                st.nextToken();
                arr[i][j]=(int)st.nval;
            }
        }
        dfs(1,0,0,0,0);
        System.out.println(ans);
         
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值