1028 追债之旅 分层图或dp写法思路 最短路

 链接:https://ac.nowcoder.com/acm/problem/14700
来源:牛客网

题目描述

小明现在要追讨一笔债务,已知有n座城市,每个城市都有编号,城市与城市之间存在道路相连(每条道路都是双向的),经过任意一条道路需要支付费用。小明一开始位于编号为1的城市,欠债人位于编号为n的城市。小明每次从一个城市到达另一个城市需要耗时1天,而欠债人每天都会挥霍一定的钱,等到第k天后(即第k+1天)他就会离开城n并再也找不到了。小明必须要在他离开前抓到他(最开始为第0天),同时希望自己的行程花费和欠债人挥霍的钱的总和最小,你能帮他计算一下最小总和吗?


输入描述:

第1行输入三个整数n,m,k,代表城市数量,道路数量和指定天数
第2-m+1行,每行输入三个整数u,v,w,代表起点城市,终点城市和支付费用。(数据保证无重边,自环)
第m+2行输入k个整数,第i个整数ai代表第i天欠债人会挥霍的钱。
数据保证:0<n≤1000,0<m≤10000,0<k≤10,1≤u,v≤n,0<w,ai≤1000

输出描述:

输出一行,一个整数,代表小明的行程花费和欠债人挥霍的钱的最小总和,如果小明不能抓住欠债人(即不能在第k天及之前到达城n),则输出-1。
示例1

输入

复制
3 3 2
1 3 10
1 2 2
2 3 2
3 7

输出

复制
13

说明

小明从1-3,总共费用=10(行程)+3(挥霍费用)=13,是方案中最小的(另一条方案花费14)。
示例2

输入

复制
3 2 1
1 2 3
2 3 3
10

输出

复制
-1

说明

小明无法在第1天赶到城3,所以输出-1。

分析

经过几天到达 n 的最短路。

每条边,都有第0天到第1 天,.....,第 k - 1 天 到第 k 天的边。分层建图

for(int i = 0;i<=k-1;i++)add(u + i * n,v + (i+1) * n,w);

 

最后计算的时候 从第一天开始枚举到第 k 天找最短路

for(int k = 1;k<=n;k++) ans = min(ans,sum[i] + dist[i * n + n]);


只要跑一边最短路,然后枚举每一天到达 n 的最短路 就可以了。

//-------------------------代码----------------------------

#define int ll
const int N = 2e5+10;
int n,m,k;
int e[N],ne[N],w[N],h[N],idx;
int a[N];
void add(int a,int b,int c) {e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++ ;}
int dist[N];
bool vis[N];
void dij() {
    ms(dist,0x3f);
    ms(vis,0);
    priority_queue<pii,V<pii>,greater<pii>> q;
    q.push({0,1});
    dist[1] = 0;
    while(q.size()) {
        auto tmp = q.top();q.pop();
        int t = tmp.second;
        if(vis[t]) continue;
        vis[t] = 1;
        for(int i = h[t];~i;i=ne[i]) {
            int j = e[i],W = w[i];
            if(dist[j] > dist[t] + W) {
                dist[j] = dist[t] + W;
                q.push({dist[j],j});
            }
        }
    }
}

void solve()
{
    cin>>n>>m>>k;
    ms(h,-1);
    fo(i,1,m) {
        int u,v,w;cin>>u>>v>>w;
        fo(j,0,k-1) {
            add(j * n + u,(j + 1) * n + v,w);
        }
        fo(j,0,k-1) {
            add(j * n + v,(j + 1) * n + u,w);
        }        
    }
    fo(i,1,k) {
        cin>>a[i];
        a[i] += a[i-1];
    }
    dij();
    int ans = inf;
    fo(i,1,k) {
        ans = min(ans,a[i] + dist[i * n + n]);
    }
    if(ans == inf) cout<<-1<<endl;
    else cout<<ans<<endl;
}
void main_init() {}
signed main(){
    AC();clapping();TLE;
    cout<<fixed<<setprecision(12);
    main_init();
//  while(cin>>n,n)
//  while(cin>>n>>m,n,m)
//    int t;cin>>t;while(t -- )
    solve();
//    {solve(); }
    return 0;
}

/*样例区


*/

//------------------------------------------------------------

 

dp写法参照之前的一道题,就是 dp[i][k] 表示枚举到 i 节点,用了 k 天的最短路

一样的

 

posted @ 2022-08-24 15:27  er007  阅读(43)  评论(0编辑  收藏  举报