zoukankan      html  css  js  c++  java
  • 洛谷P2939 [USACO09FEB]改造路Revamping Trails(分层图最短路)

    题意翻译

    约翰一共有N)个牧场.由M条布满尘埃的小径连接.小径可 以双向通行.每天早上约翰从牧场1出发到牧场N去给奶牛检查身体.

    通过每条小径都需要消耗一定的时间.约翰打算升级其中K条小径,使之成为高 速公路.在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为0.

    请帮助约翰决定对哪些小径进行升级,使他每天从1号牧场到第N号牧场所花的时间最短

    题目描述

    Farmer John dutifully checks on the cows every day. He traverses some of the M (1 <= M <= 50,000) trails conveniently numbered 1..M from pasture 1 all the way out to pasture N (a journey which is always possible for trail maps given in the test data). The N (1 <= N <= 10,000) pastures conveniently numbered 1..N on Farmer John's farm are currently connected by bidirectional dirt trails. Each trail i connects pastures P1_i and P2_i (1 <= P1_i <= N; 1 <= P2_i <= N) and requires T_i (1 <= T_i <= 1,000,000) units of time to traverse.

    He wants to revamp some of the trails on his farm to save time on his long journey. Specifically, he will choose K (1 <= K <= 20) trails to turn into highways, which will effectively reduce the trail's traversal time to 0. Help FJ decide which trails to revamp to minimize the resulting time of getting from pasture 1 to N.

    TIME LIMIT: 2 seconds

    输入格式

    * Line 1: Three space-separated integers: N, M, and K

    * Lines 2..M+1: Line i+1 describes trail i with three space-separated integers: P1_i, P2_i, and T_i

    输出格式

    * Line 1: The length of the shortest path after revamping no more than K edges

    说明/提示

    K is 1; revamp trail 3->4 to take time 0 instead of 100. The new shortest path is 1->3->4, total traversal time now 1.


    这道题是一道分层图最短路的入门题。
    首先说一下分层图最短路,以下内容引自https://www.luogu.org/blog/xiaohou/(侵删)

    第一种思想:dp(动态规划)

    一般模型:

    在一张图上,有k次机会可以通过一条边而不需要计算权值,求从起点到终点的最短路。

    思想:

    将一个点拆分为k+1个点,分别表示到这个点时,免费次数消耗了0次、1次...k次

    这样就可以把这k个点想象成对应dp的不同状态

    dis[i][j]表示到第i个点时,消耗了j次免费机会多的最短路

    to 要到达的点 ; f 父亲节点

    dis[to][j]=min(dis[x][j]+val(x,to),dis[x][j-1])

    因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态

    如果我们消耗了免费机会

    dis[to][j] = min{dis[x][j - 1]}

    如果我们没消耗免费过路权

    dis[to][j] = min{dis[x][j] + val(x, to)}

    这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏

    也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权

    到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次....用了k次

    也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了

    然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路

    第二种思想:分层图


    而这道题方法就是将每一个节点拆成(k+1)个,分别代表用了0,1,2·····k种修建高速公路的次数,将每层图连起来,根据题意可知每层图之间的边权为0,这里有一个细节需要注意一下:连边只能从上面 (j-1) 层连到 j 层,因为我们用了这次机会后没办法再退回去。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define R register
    #define maxn 1000010
    #define IL inline
    #define ll long long
    #define M(x,y) make_pair(x,y)
    #define clear(a) memset(a,0,sizeof a)
    
    int n,m,k,tot;
    ll ans;
    int first[maxn],vis[maxn];
    ll dis[maxn];
    struct edge{
        int nextx,to,val;
    }e[maxn*10];
    priority_queue< pair<int,int> >Q;
    
    IL void add(int u,int v,int w)
    {
        tot++;
        e[tot].nextx=first[u];
        first[u]=tot;
        e[tot].to=v;
        e[tot].val=w;
    }
    
    IL void dij(int x)
    {
        memset(dis,0x3f,sizeof dis);
        dis[x]=0;
        Q.push(M(0,x));
        while(!Q.empty())
        {
            int u=Q.top().second;
            Q.pop();
            if(vis[u])continue;
            vis[u]=1;
            for(R int i=first[u];i;i=e[i].nextx)
            {
                int v=e[i].to,w=e[i].val;
                if(dis[v]>dis[u]+w)
                    dis[v]=dis[u]+w,Q.push(M(-dis[v],v));
            }
        }
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(R int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w),add(v,u,w);
            for(R int j=1;j<=k;j++)
            {
                add(j*n+u,j*n+v,w),add(j*n+v,j*n+u,w);
                add((j-1)*n+u,j*n+v,0),add((j-1)*n+v,j*n+u,0);//连边只能从上面 (j-1) 层连到 j 层,因为我们用了这次机会后没办法再退回去
            }
        }
        dij(1);
        ans=dis[n];
        for(int i=1;i<=k;i++)
            ans=min(ans,dis[i*n+n]);
        cout<<ans<<"
    ";
        return 0;
    }
  • 相关阅读:
    python中的反射
    ZOJ 3827 Information Entropy 水
    我的软考之路(七)——数据结构与算法(5)之查找
    nginx.conf 集群完整配置
    恼人的函数指针(二)
    C语言100个经典的算法
    spring事务心得积累
    Vue报错:OPTIONS 405 Method Not Allowed以及CORS跨域错误
    IDA脚本dump内存的示例
    lightProxy
  • 原文地址:https://www.cnblogs.com/KGW-/p/11336604.html
Copyright © 2011-2022 走看看