【LuoguP1772】 [ZJOI2006]物流运输

题目链接

题目描述

物流公司要把一批货物从码头A运到码头B。由于货物量比较大,需要n天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。但是修改路线是—件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个n天的运输计划,使得总成本尽可能地小。
(1n100)m(1m20) ( 1 ≤ n ≤ 100 ) 、 m ( 1 ≤ m ≤ 20 )
题意:
给你一张图要求你走n次的从1到m的路径,每次有一些点不可走,相邻两次若路径不同则有额外代价,求最小代价。

题解

看到 m 只有 20 ,可以想到状压,但本人太弱不知道如何很好的实现。
于是我们就来普通的dp吧。
这里一个很烦人的点就是每次都有点不能走,给我们判断最优解带来很大困扰,所以状态的设计至关重要。
假设我们一天天来看,我们不知道今天这条路径应该怎样走,因为合并两个相邻天的答案很困难,且由于可能有多种不同的最短路存在,我们不好判断最优解。
所以我们应该将一段天看成一个整体来跑最短路。所以很容易设出以下状态:
dp[i][j]ij 设 d p [ i ] [ j ] 表 示 第 i 天 到 第 j 天 跑 同 一 条 最 短 路 所 需 的 最 小 代 价
转移的话再在前面取一天进行转移就行了,具体可以看代码。

时间复杂度应该是 O(n3) O ( n 3 ) ,但不满。

代码如下:

#include
#include
#include
#include
#include
#include
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
const int M=300;
const int N=200;
int n;
int m,k,e;;
int INF;
int dis[N][N];
bool cant[N][M];
int dp[N][N];
queue<int> Q;
int Dis[N];
bool go[N];
bool in[N];
inline int SPFA(int s,int t)
{
    Set(Dis,127/3);
    Set(go,0);
    for(register int i=1;i<=m;i++){
        for(register int j=s;j<=t;j++){
            if(cant[i][j]) {go[i]=1;break;}
        }
    }
    Q.push(1);
    Dis[1]=0;in[1]=1;
    while(!Q.empty())
    {
        register int u=Q.front();Q.pop();
        for(register int v=1;v<=m;v++){
            if(v==u||go[v]||dis[u][v]==INF) continue;
            if(Dis[v]>Dis[u]+dis[u][v]){
                Dis[v]=Dis[u]+dis[u][v];
                if(!in[v]) Q.push(v),in[v]=1;
            }
        }
        in[u]=0;
    }
    return Dis[m];
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&k,&e);
    if(m==1){
        puts("0");return 0;
    }
    register int x,y,w;
    Set(dis,127/3);
    Set(dp,127/3);
    INF=dis[0][0];
    for(register int i=1;i<=e;i++){
        scanf("%d%d%d",&x,&y,&w);
        dis[x][y]=dis[y][x]=min(dis[x][y],w);
    }
    int d;
    scanf("%d",&d);
    for(register int i=1;i<=d;i++){
        scanf("%d %d %d",&x,&y,&w);
        if(y>w) swap(y,w);
        for(register int j=y;j<=w;j++){
            cant[x][j]=1;
        }
    }
    int ans=INF;
    dp[0][0]=0;
    for(register int i=1;i<=n;i++){
        for(register int j=i;j<=n;j++){
            d=SPFA(i,j);//i到j天跑同一条
            if(d==INF) break;//不可行则break掉,后面的一定也不可行
            for(register int K=i-1;K>=0;K--){//枚举前面从哪开始跑的
                if(dp[K][i-1]==INF) break;//这里不行了也break掉,同理
                dp[i][j]=min(dp[i][j],dp[K][i-1]+k+d*(j-i+1));
            }
            if(j==n) ans=min(ans,dp[i][j]);//到了第n天则统计答案
        }
    }
    printf("%d\n",ans-k);//最后答案要减k,我们一开始把第0天也看成跑了一次不同的路径。
}

你可能感兴趣的:(======题解======,——图论———,——动态规划——)