题干:
物流公司要把一批货物从码头 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\n",dp[1][n]);
87 }