P1772 [ZJOI2006]物流运输(线性dp+最短路径)

本文介绍了一个物流运输问题的解决方法,目标是最小化运输成本。通过动态规划与最短路径算法结合,解决了特定条件下需要调整运输路线的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P1772 [ZJOI2006]物流运输

题意

[ZJOI2006]物流运输

题目描述

物流公司要把一批货物从码头 A 运到码头 B。由于货物量比较大,需要 n n n 天才能运完。货物运输过程中一般要转停好几个码头。

物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。

但是修改路线是—件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个 n n n 天的运输计划,使得总成本尽可能地小。

输入格式

第一行是四个整数 n , m , k , e n,m,k,e n,m,k,e n n n 表示货物运输所需天数, m m m 表示码头总数, k k k 表示每次修改运输路线所需成本, e e e 表示航线条数。

接下来 e e e 行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度。其中码头 A 编号为 1 1 1,码头 B 编号为 m m m。单位长度的运输费用为 1 1 1。航线是双向的。

再接下来一行是一个整数 d d d,后面的 d d d 行每行是三个整数 p , a , b p,a,b p,a,b。表示编号为 p p p 的码头在 [ a , b ] [a,b] [a,b] 天之内无法装卸货物。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头 A 到码头 B 的运输路线。

输出格式

包括了一个整数表示最小的总成本。
总成本为 n n n 天运输路线长度之和 + k × +k\times +k× 改变运输路线的次数。

样例 #1
样例输入 #1
5 5 10 8
  1 2 1
  1 3 3
  1 4 2
  2 3 2
  2 4 4
  3 4 1
  3 5 2
  4 5 2
  4
  2 2 3
  3 1 1
  3 3 3
  4 4 5
样例输出 #1
32
提示

【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1n100 1 ≤ m ≤ 20 1\le m \le 20 1m20

【样例输入说明】

上图依次表示第 1 1 1 至第 5 5 5 天的情况,阴影表示不可用的码头。

【样例输出说明】

前三天走 1 → 4 → 5 1 \to 4 \to 5 145,后两天走 1 → 3 → 5 1 \to 3 \to 5 135,这样总成本为 ( 2 + 2 ) × 3 + ( 3 + 2 ) × 2 + 10 = 32 (2+2)\times 3+(3+2)\times 2+10=32 (2+2)×3+(3+2)×2+10=32

_NOI导刊2010提高(01)


tags

图论:最短路径,线性dp

思路

  1. 距离:最短路径,最优解:dp
  2. 动态转移方程:dp[i]=min(dp[i],dp[j-1]+(i-j+1)*l+k)
    1. dp[i]表示前i天的最短花费
    2. 第i天有两种选择:继续原先的路(不能走了必须的换路)、换一条路径(最短路径)
    3. 对于第i天,我们可以选择第i天换路径,第i-1天换路径,第i-2天换路径······第j天换路径
    4. 假如对于第j天换路径,那么我们最小花费是第j天到第i天走最短路径(注意此最短路径是被第j天到第i天所有不能走的所有的点限制的),所有j到哪天为止呢?不能在换路径为止:即第j-1天到第i天所有不能走的点的限制使其不能从1到m了
    5. 所以对于求dp[i],我们用j从j=i枚举到j=1,然后枚举过程中逐天加上不能走的点的限制,更新最短路径,直到不能换路径为止
    6. 注意开long long
    7. 注意dp[0]=-k,因为从第1天开始的话不用去换路径。

AC代码

  1. 最短路求法提供spfa和dijkstra两种算法
  2. 用noallow来存每天不能走的点,now存第j天到第i天不能走的点(逐天添加点)
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>P;
const int maxn=105,maxm=25,maxe=1005;
int n,m,k,e,d;
int head[maxm],dis[maxm],vis[maxm];
int cnt;
ll dp[maxn];
int noallow[maxn][maxm],g[maxm];

struct Edge{
    int to,cost,next;
}edge[maxe];

void addedge(int u,int v,int w){
    edge[++cnt].to=v;
    edge[cnt].cost=w;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

// int spfa(){
//     memset(dis,0x3f,sizeof(dis));
//     memset(vis,0,sizeof(vis));
//     dis[1]=0;
//     vis[1]=1;
//     queue<int>q;
//     q.push(1);
//     while(!q.empty()){
//         int x=q.front();
//         q.pop();
//         vis[x]=0;
//         for(int i=head[x];i;i=edge[i].next){
//             int too=edge[i].to,costt=edge[i].cost;
//             if(!g[too]&&dis[too]>dis[x]+costt){
//                 dis[too]=dis[x]+costt;
//                 if(!vis[too]){
//                     q.push(too);
//                     vis[too]=1;
//                 }
//             }
//         }
//     }
//     return dis[m];
// }

int dijkstra(){
    priority_queue<P,vector<P>,greater<P>>q;
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    q.push(P(0,1));
    while(!q.empty()){
        int pos=q.top().second;
        q.pop();
        if(vis[pos])continue;
        vis[pos]=1;
        for(int i=head[pos];i;i=edge[i].next){
            int too=edge[i].to,costt=edge[i].cost;
            if(g[too])continue;
            if(!vis[too]&&dis[too]>dis[pos]+costt){
                dis[too]=dis[pos]+costt;
                q.push(P(dis[too],too));
            }
        }
    }
    return dis[m];
}

int main(){
    cin>>n>>m>>k>>e;
    for(int i=0;i<e;i++){
        int u,v,w;
        cin>>u>>v>>w;
        addedge(u,v,w);
        addedge(v,u,w);
    }
    cin>>d;
    for(int i=0;i<d;i++){
        int p,a,b;
        cin>>p>>a>>b;
        for(int j=a;j<=b;j++)noallow[j][p]=1;
    }
    
    memset(dp,0x3f,sizeof(dp));
    dp[0]=-k;
    for(int i=1;i<=n;i++){
        memset(g,0,sizeof(g));
        for(int j=i;j>=1;j--){
            for(int k=1;k<=m;k++){
                if(noallow[j][k])g[k]=1;
            }
            int l=dijkstra();
            if(l==inf)break;
            dp[i]=min(dp[i],dp[j-1]+(i-j+1)*l+k);
        }
    }
    cout<<dp[n]<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值