bzoj 1003 物流运输 最短路+dp

【题意】:一共要运输n天物资,每天都要从1号码头运送到m号码头,给你e行,每行告诉你从a号码头到b号码头的花费(无向图)。再告诉你d行,表示p号码头,在第 [a,b] 天内不能经过。当你某一天和上一天的运输路线不同时,需要额外花费k。现在让你求这n天加起来最小的总花费。

n<=20, m<=100

【题解】:dp[i]代表前 i 天的最小花费,那么dp[i] 一定是从某一个dp[j] 转移过来,并且[ j+1,i ]这些天里走的是同一条路。用c[a,b] 表示[a,b]天内走同一条路的最小花费。

那么可以得出dp方程: dp[i] = min(dp[j-1] + c[j,i])

c[ ][ ]只要先预先跑一遍最短路即可。

【代码】:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k,e,a,b,C;
ll c[25][25];
bool fre[25][105],vis[105];
ll cost[105][105];
ll inf=(1ll<<63)-1;
ll dp[105];
void dijkstra(int l,int r)
{
    for(int i=0;i<=100;i++) vis[i]=0;
    set<pair<ll,int> >st;
    st.clear();
    int flag=0;
    st.insert(make_pair(0,1));
    while(!st.empty()){
        int x=st.begin()->second;
        ll f=st.begin()->first;
        st.erase(st.begin());
        if(vis[x]==1) continue;
        vis[x]=1;
        if(x==m){
            flag=1;
            cost[l][r]=f;
            break;
        }
        for(int i=0;i<=m;i++){
            if(vis[i]==0&&c[x][i]){
                int flag2=0;
                for(int j=l;j<=r;j++){
                    if(fre[i][j]==1){
                        flag2=1;
                        break;
                    }
                }
                if(flag2==0){
                    st.insert(make_pair(f+c[x][i],i));
                }
            }
        }
    }
    if(flag==0){
        cost[l][r]=inf;
    }
}
int main(void)
{

    scanf("%d%d%d%d",&n,&m,&k,&e);
    for(int i=0;i<e;i++){
        scanf("%d%d%d",&a,&b,&C);
        if(c[a][b]==0) c[a][b]=c[b][a]=C;
        else c[a][b]=c[b][a]=min((ll)C,c[a][b]);
    }
    int d;
    scanf("%d",&d);
    for(int i=0;i<d;i++){
        scanf("%d%d%d",&C,&a,&b);
        for(int j=a;j<=b;j++){
            fre[C][j]=1;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            dijkstra(i,j);
        }
    }
    dp[0]=-k;//第一天不用换路线
    for(int i=1;i<=n;i++){
        dp[i]=inf;
        for(int j=1;j<=i;j++){
            if(cost[j][i]<inf)
            dp[i]=min(dp[i],dp[j-1]+cost[j][i]*(i-j+1)+k);
        }
    }
    printf("%lld\n",dp[n]);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值