zoukankan      html  css  js  c++  java
  • 【题解】物流运输 [ZJ2006] [P1772] [BZOJ1003]

    【题解】物流运输 [ZJ2006] [P1772] [BZOJ1003]

    传送门:物流运输 ([ZJ2006]) ([P1772]) ([BZOJ1003])

    【题目描述】

    给定一个含 (e) 条带权边的无向图,每天都需要从结点 (1) 走到结点 (m),一共要走 (n) 天,每天的花费为所经过路径的权值和。一般来说会选择一条路线每天都这样走下去,但某些结点会在连续的几天内都无法经过,这时候就需要改变路线。每次改变路线都有 (K) 的额外花费。求最小总花费。
    (即:总花费 (=) (n) 天经过路径花费和 (+) (K*) 改变路径次数)

    【输入】

    第一行四个整数 (n,m,K,e),后面 (e) 行表示每条边所连接的结点编号和边权。接下来一个整数 (T) 表示有 (T) 个结点会在某一时段无法通过,后面每行三个整数分别表示节点编号,无法通过的天数起点和终点。

    【输出】

    一个整数表示最小花费。

    【样例】

    样例输入:
    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
    
    样例输出:
    32
    

    【数据范围】

    (100\%) (1 leqslant N leqslant 100,) (1 leqslant m leqslant 20)


    【分析】

    暂时先不考虑如何计算路径花费的问题,就先视作 (w) 好了。
    很明显,状态转移的切入点在于路径的改变。用 (dp[i]) 表示走完前 (i) 天所需最小花费,设最后一次改变路径后使用的天数为 ([j+1,i]),那么 (dp[i]=min{dp[j]+K+w*(i-j)})

    一开始看到这个式子还以为是斜率优化,但实际上并不需要,这么良心的数据直接暴力就能过。

    (w) 可以使用 (dijkstra) 或者 (SPFA),不能走的结点就提前记录一下。可以用 (no[i][j]) 表示 (i)(j) 这天不可走,转移状态时倒序枚举 (j)。由于 (j+1) 这天不可经过的点在 (j) 这天还是不可经过,这样做就可以省掉一些麻烦。

    另外,如果 (inf) 设太大的话,两个 (inf) 相乘会爆 (long) (long),要特判一下。

    初始化有两种方法:(dp[i]=w*i(i in [1,n])) 或者 (dp[0]=-K),如果是后者,在第 (0) 天后改变路径并加上 (K) 的花费,实际上就是 (0) 花费选出了第一条路径。

    时间复杂度为:(O(n^2mlogm))


    【Code】

    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register LL
    using namespace std;
    const LL N=103,M=403,inf=1e18;
    LL n,m,e,o,x,y,z,K,to,dp[N],dis[N],pan[N],can[N],head[N],no[N][N];
    struct QAQ{LL w,to,next;}a[M<<1];
    struct QWQ{LL x,d;inline bool operator<(QWQ o)const{return d>o.d;}};
    priority_queue<QWQ>Q;
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline void add(Re x,Re y,Re z){a[++o].to=y,a[o].w=z,a[o].next=head[x],head[x]=o;}
    inline LL dijkstra(){
        for(Re i=0;i<=m;++i)pan[i]=0;
        for(Re i=0;i<=m;++i)dis[i]=inf;
        pan[1]=0,Q.push((QWQ){1,dis[1]=0});
        while(!Q.empty()){
            x=Q.top().x,Q.pop();
            if(pan[x]||can[x])continue;
            pan[x]=1;
            for(Re i=head[x];i;i=a[i].next)
                if(dis[to=a[i].to]>dis[x]+a[i].w){
                	dis[to]=dis[x]+a[i].w;
                	Q.push((QWQ){to,dis[to]});
                }
        }
        return dis[m];
    }
    int main(){
        in(n),in(m),in(K),in(e);
        while(e--)in(x),in(y),in(z),add(x,y,z),add(y,x,z);
        in(e);
        while(e--){
        	in(x),in(y),in(z);
        	for(Re i=y;i<=z;++i)no[x][i]=1;
        }
        for(Re i=0;i<=n;++i)dp[i]=inf;dp[0]=-K;
        for(Re i=1;i<=n;++i){
        	for(Re k=1;k<=m;++k)can[k]=0;
        	for(Re j=i-1;j>=0;--j){
                for(Re k=1;k<=m;++k)can[k]|=no[k][j+1];
                Re tmp=dijkstra();
                if(tmp!=inf)dp[i]=min(dp[i],dp[j]+K+(i-j)*tmp);
        	}
        }
        printf("%lld",dp[n]);
    }
    
  • 相关阅读:
    2017-5-15 winform项目总结(知识点补充)
    2017-5-7 time控件 三级联动(省,市,区)
    2017-5-4 进程 线程 用户控件
    2017-5-3 打印控件 MDI 窗体容器 Activated事件
    2017-5-2 对话框控件 MessageBox.Show()用法补充 打开新窗体的3中模式
    窗体移动 窗体阴影API
    2017-4-28 ListView控件学习
    【2017-03-28】JS基础、DOM操作
    【2017-03-24】样式表样式
    【2017-03-24】CSS样式表
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/11352722.html
Copyright © 2011-2022 走看看