[DP] BZOJ1003: [ZJOI2006]物流运输

题意

给出一个m个的无向图,你需要规划n天的运输方案。n天里,每天都要从S走到T走一次,每个点在某些天里不能经过。每天需要花费等于路径长度的代价,而且如果第i天选择的路径与第i-1天不同(i>1),还需要多花K的修改路线代价。求最小总代价。
(n<=100,m<=20)

题解

脑洞题,不算太难。可能一开始会想的比较远,如果考虑路径的变化,发现难以下手。
实际上是个简单的线性DP。设f[i]表示前i天花费的总代价
转移方程:f[i]=min{f[j]+w(j+1,i)+K}
其中w(i,j)表示第i~j天都走同一条路的最短路径长,刷最短路即可。
可能会有疑问:f[i]这个状态并没有包含路径的信息啊,为什么不用判断w(j+1,i)走的路与f[j]是否相同呢?简单想一想就会发现这个不会影响到答案。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=405;
int n,m,w_c,e,f[maxn],dis[maxn],que[10005];
int fir[maxn],nxt[maxn],son[maxn],w[maxn],tot;
bool vis[maxn],ac[maxn][maxn],now[maxn];

bool add(int x,int y,int z){
    son[++tot]=y; w[tot]=z; nxt[tot]=fir[x]; fir[x]=tot;
}
int spfa(){
    if(!(now[1]&&now[m])) return -1;
    memset(vis,0,sizeof(vis));
    memset(dis,63,sizeof(dis)); int INF=dis[0];
    int head=0,tail=1;
    dis[1]=0; que[1]=1;
    while(head!=tail){
        int x=que[++head];
        vis[x]=false;
        for(int j=fir[x];j;j=nxt[j]) if(now[son[j]]&&dis[x]+w[j]<dis[son[j]]){
            dis[son[j]]=dis[x]+w[j];
            if(!vis[son[j]]) vis[son[j]]=true, que[++tail]=son[j];
        }
    }
    return (dis[m]==INF)?-1:dis[m];
}
int main(){
    scanf("%d%d%d%d",&n,&m,&w_c,&e);
    for(int i=1;i<=e;i++){
        int x,y,z; scanf("%d%d%d",&x,&y,&z);
        add(x,y,z); add(y,x,z);
    }
    memset(ac,1,sizeof(ac));
    int t; scanf("%d",&t);
    while(t--){
        int id,L,R; scanf("%d%d%d",&id,&L,&R);
        for(int i=L;i<=R;i++) ac[id][i]=false;   
    }
    memset(f,63,sizeof(f)); f[0]=-w_c;
    for(int i=1;i<=n;i++){
        memset(now,1,sizeof(now));
        for(int j=i;j>=1;j--){
            for(int k=1;k<=m;k++) if(!ac[k][j]) now[k]=false;
            int res=spfa(); if(res==-1) break;
            f[i]=min(f[i],f[j-1]+res*(i-j+1)+w_c);
        }   
    }
    printf("%d",f[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值