[ZJOI2006]物流运输(最短路 + 区间dp)

题干:
  物流公司要把一批货物从码头 A 运到码头 B。由于货物量比较大,需要 n 天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输线路,让货物能够按时到达目的地。但是修改路线是一件十分麻烦的事情,会带来额外的成本,因此物流公司希望能够订一个 n 天的运输计划,使得总成本尽可能地小。

  第一行是四个整数 n(1≤n≤100),m(1≤m≤20),K,e。n 表示货物运输所需天数,m 表示码头总数,K 表示每次修改运输路线所需成本,接下来 e 行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度 (>0)。其中码头 A 编号为 1,码头 B 编号为 m。单位长度的运输费用为 1。航线是双向的。再接下来一行是一个整数 d,后面的 d 行每行是三个整数 P(1<P<m),a,b(1≤a≤b≤n)。表示编号为 P 的码头从第 a 天到第 b 天无法装卸货物(含头尾)。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头 A 到码头 B 的运输路线。
  包括了一个整数表示最小的总成本。总成本 = n 天运输路线长度之和 + K × 改变运输路线的次数。

 



题解:

  先看一下题:本题让求出1~n天运输路线所花费的费用,表示一头雾水。。。题干中还给出,哪几个点在a~b区间范围内不可被使用,又一头雾。。。其实我们再回头看一下,1~n天的费用就是:

  1、1~x天的第一种路线费用  +  x+1天的第二种的路线费用  +   改变路线的费用

  2、1~x天的第一种路线费用  +  x+1天的第一种的路线费用 (其实这种就可以合并)。

  这样分出情况来看,我们好像就可以设出状态转移方程:

  dp[i][j]表示i~j天期间的最小路线费用(这就意味着后面所说的最短路均为从1~n的最短路,我们在此将每一天作为一个单位),那么:

dp[1][n]=dp[1][x]+dp[x+1][y]+......+dp[z+1][n] + t*K

  
  这不就是区间dp吗?有一股正解的气息。。。

  我们来考虑一下初始化的问题(假设dp[i][j]):

  1、i==j时,就是跑一个有不能经过的点的最短路的长度(权值为一嘛)。

  2、i > j时,无解(既然跑最短路,返程肯定不需要)

  3、i < j时,毕竟是初始化,我们就假设它在i~j一段时间内没有转变路线,那么凡是在i~j期间有节点不能用,那么这个节点就一定不可能存在于现在的最短路中。我们将那几个点不可用的点标记后再跑一个最短路即可。(注意结果要乘上时间长度(j-i+1))

    我们为什么要将dp[i][j](i < j)初始化为没有转变路线的情况呢?其实这与我们后面的dp转移有关。后面的转移一定不会再考虑大片的具有相同路径的连续几天了,我们只会考虑在哪一天换一下路线使总花费变小。所以就需要在初始化时将这种可能成为答案的、大片的具有相同路径的连续几天的情况了。如果这i~j天不能用的港口太多,导致无解,那就让它无解吧,反正到后面合并时会更新,就不考虑这种情况了。

  有了上面的初始化( Dijkstra 与 spfa 都可以),我们就来写一下区间dp的式子(其实跟普通区间dp没两样):

dp[i][j]=Σj-1q=imin(dp[i][j],dp[i][q]+dp[q+1][j]+k);

  got it!
Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 #define $ 210
 5 using namespace std; 
 6 int n,m,k,e,first[$],tot,d,dp[$][$],lu[$/5];
 7 bool judge[$][$/5],vis[$],pro[$];
 8 struct tree{    int to,next,w;    }a[$*2];
 9 inline int min(int x,int y){    return x<y?x:y;    }
10 inline void add(int x,int y,int w){
11     a[++tot]=(tree){    y,first[x],w    };
12     first[x]=tot;
13     a[++tot]=(tree){    x,first[y],w    };
14     first[y]=tot;
15 }
16 inline int Dijkstra(int start,int done){
17     priority_queue< pair<int,int> > q;
18     memset(vis,0,sizeof(vis));
19     memset(lu,63,sizeof(lu));
20     memset(pro,0,sizeof(pro));
21     for(register int i=start;i<=done;++i)
22         for(register int j=1;j<=m;++j)  if(judge[i][j]) pro[j]=1;
23     lu[1]=0;
24     q.push(make_pair(0,1));
25     while(q.size()){
26         int x=q.top().second; q.pop();
27         if(vis[x]) continue;
28         vis[x]=1;
29         for(register int i=first[x];i;i=a[i].next){
30             int to=a[i].to,w=a[i].w;
31             if(pro[to]) continue;
32             if(lu[to]>lu[x]+w){
33                 lu[to]=lu[x]+w;
34                 q.push(make_pair(-lu[to],to));
35             }
36         }
37     }
38     return lu[m];
39 }
40 inline int spfa(int start,int done){
41     queue<int> q;
42     memset(vis,0,sizeof(vis));
43     memset(lu,63,sizeof(lu));
44     memset(pro,0,sizeof(pro));
45     for(register int i=start;i<=done;++i)
46         for(register int j=1;j<=m;++j)  if(judge[i][j]) pro[j]=1;
47     q.push(1); lu[1]=0;
48     vis[1]=1;
49     while(q.size()){
50         int x=q.front(); q.pop();
51         vis[x]=0;
52         for(register int i=first[x];i;i=a[i].next){
53             int to=a[i].to,w=a[i].w;
54             if(pro[to]) continue;
55             if(lu[to]>lu[x]+w){
56                  lu[to]=lu[x]+w;
57                  if(!vis[to]) q.push(to),vis[to]=1;
58             }
59         }
60     }
61     return lu[m];
62 }
63 signed main(){
64     memset(dp,63,sizeof(dp)); 
65     scanf("%d%d%d%d",&n,&m,&k,&e);
66     for(register int i=1,x,y,z;i<=e;++i)
67         scanf("%d%d%d",&x,&y,&z),add(x,y,z);
68     scanf("%d",&d);
69     for(register int i=1,x,l,r;i<=d;++i){
70         scanf("%d%d%d",&x,&l,&r);
71         for(register int j=l;j<=r;++j) judge[j][x]=1;
72     }
73     for(register int i=1;i<=n;++i)
74         for(register int j=i,x;j<=n;++j){ 
75             x=i%2?Dijkstra(i,j):spfa(i,j);
76             if(x<100000) dp[i][j]=x*(j-i+1);
77             else         dp[i][j]=0x7ffffff;
78         }
79     for(register int l=2;l<=n;++l){
80         for(register int i=1;i<=n-l+1;++i){
81             int j=i+l-1;
82             for(register int q=i;q<j;++q)
83                 dp[i][j]=min(dp[i][j],dp[i][q]+dp[q+1][j]+k);
84         }
85     }
86     printf("%d
",dp[1][n]);
87 }
View Code
越努力 越幸运
原文地址:https://www.cnblogs.com/OI-zzyy/p/11177941.html