洛谷 P2939

题意:每天,农夫John需要经过一些道路去检查牛棚N里面的牛. 农场上有M(1<=M<=50,000)条双向泥土道路,编号为1..M. 道路i连接牛棚P1_i和P2_i (1 <= P1_i <= N; 1 <= P2_i<= N). John需要T_i (1 <= T_i <= 1,000,000)时间单位用道路i从P1_i走到P2_i或者从P2_i 走到P1_i 他想更新一些路经来减少每天花在路上的时间.具体地说,他想更新K (1 <= K <= 20)条路经,将它们所须时间减为0.帮助FJ选择哪些路经需要更新使得从1到N的时间尽量少.

思路:分层最短路。拆点最短路,将每个点拆成k个点来看待。

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<LL,int> pii;
typedef pair<pii,int> piii;
const int maxn = 10005;
const int maxk = 25;
const int maxm = 100005;
int he[maxn],ver[maxm],ne[maxm],tot,cost[maxm];
void add( int x,int y,int w ){
    ver[++tot] = y;
    ne[tot] = he[x];
    he[x] = tot;
    cost[tot] = w;
}
int vis[maxn][maxk];
LL dis[maxn][maxk],K;
priority_queue< piii,vector<piii>,greater<piii> > Q;
void dijkstra(int s){
    memset( dis,0x3f,sizeof(dis) );
    dis[s][0] = 0;
    Q.push( piii( pii(0,0),s ) );
    while(Q.size()){
        int x = Q.top().se;
        int k = Q.top().fi.se;
        Q.pop();
        if( vis[x][k] ) continue;
        vis[x][k] = 1;
        for( int cure=he[x];cure;cure = ne[cure] ){
            int y = ver[cure];
            if( vis[y][k] ) continue;
            if( dis[y][k] > dis[x][k] + cost[cure] ){
                dis[y][k] = dis[x][k] + cost[cure];
                Q.push( piii( pii( dis[y][k],k ),y ) );
            }
        }
        for( int cure = he[x];cure;cure = ne[cure] ){
            int y = ver[cure];
            if( vis[y][k] || k == K ) continue;
            if( dis[y][k+1] > dis[x][k] ){
                dis[y][k+1] = dis[x][k];
                Q.push( piii( pii( dis[y][k+1],k+1 ),y ) );
            }
        }
    }
}
int main(){
    int n,m,x,y,w;
    scanf("%d%d%d",&n,&m,&K);
    for( int i = 1;i <= m;i++ ){
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);add(y,x,w);
    }
    dijkstra(1);
    LL ans=0x3f3f3f3f3f3f3f3f;
    for( int i = 0;i <= K;i++ ){
        ans = min( ans,dis[n][i] );
    }
    printf("%d\n",ans);
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值