BZOJ 1003.[ZJOI2006]物流运输

题目大意

给定一个图,其中有一个是起点,有一个是终点,有货物分n天运输完,已知每一天哪些点开,哪些点闭。每一天要找到一条最短路运输货物。若要改动运输路径,每次改动需要付出一定的代价k。

解题思路

最难解脱的是,可能会想到网络流方面(数据范围比较小)。但这个怎么解决呢?
<留坑>
如果想到用DP做,考虑有什么东西可以利用的。
①每一天要选一条路径,即给一定的代价。
②一段区间内,相同的运输路径路径相同。
③改变运输路径,相当于新的一个时段开始了。k起到了一个时间轴上界点的作用。
④最短路径的长度对于一段连续的时间来说是一定的。因为在这段时间内,点的开闭是没有改动的。
根据此,dp的状态只需要表示时间即可。即设 f[i] 表示1~i天的答案。
Dis[i][j] 表示 [i,j] 时间内的最短运输路径。
根据③,枚举上一个界点j,即新的那段是 (ij+1)Dis[i][j]+k .
易得dp方程为

f[i]=max(f[i],f[j1]+(ij+1)Dis[i][j]+k)

最后答案即为 f[n]k
PS:如果答案要一段段累计,那么寻找分割点是很重要的,这个地方一定要缩短思考时间,然后快速定下界点前一段答案和新一段答案究竟是什么。
这个破地方经常耗我2个小时!!!

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 106
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
    int to,next,val;
};note edge[N*N];
int tot,head[N];
int i,j,k,l,e,n,m,ans;
int sum[N][N];
int f[N],Dis[N][N];
int dis[N];
bool bz[N];
int qu[N*100];
int u,v,w,q;
void lb(int x,int y,int z){edge[++tot].to=y;edge[tot].next=head[x];edge[tot].val=z;head[x]=tot;}
int spfa(int st,int en){
    int i,l,r,x;
    memset(dis,127,sizeof(dis));
    memset(bz,0,sizeof(bz));
    bz[1]=1;dis[1]=0;qu[1]=1;
    l=0,r=1;
    while(l<r){
        x=qu[++l];
        for(i=head[x];i;i=edge[i].next){
            if(sum[edge[i].to][en]-sum[edge[i].to][st-1])continue;
            if(dis[edge[i].to]>dis[x]+edge[i].val){
                dis[edge[i].to]=dis[x]+edge[i].val;
                if(!bz[edge[i].to]){
                    bz[edge[i].to]=1;
                    qu[++r]=edge[i].to;
                }
            }
        }
        bz[x]=0;
    }
    return dis[m];
}
int main(){
    scanf("%d%d%d%d",&n,&m,&k,&e);
    fo(i,1,e){
        scanf("%d%d%d",&u,&v,&w);
        lb(u,v,w);lb(v,u,w);
    }
    scanf("%d",&q);
    while(q--){
        scanf("%d%d%d",&u,&v,&w);
        sum[u][v]++;sum[u][w+1]--;
    }
    fo(i,1,m)fo(j,1,n)sum[i][j]+=sum[i][j-1];
    fo(i,1,m)fo(j,1,n)sum[i][j]+=sum[i][j-1];
    fo(i,1,n)fo(j,i,n)Dis[i][j]=spfa(i,j);
    memset(f,127,sizeof(f));
    f[0]=0;
    fo(i,1,n)fo(j,1,i)
        if(Dis[j][i]^2139062143)
        f[i]=min(f[i],f[j-1]+(i-j+1)*Dis[j][i]+k);
    printf("%d",f[n]-k);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值