[ZJOI2006]物流运输
题目
物流公司要把一批货物从码头A运到码头B。由于货物量比较大,需要n天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。但是修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个n天的运输计划,使得总成本尽可能地小。
INPUT
第一行是四个整数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的运输路线。
OUTPUT
包括了一个整数表示最小的总成本。总成本=n天运输路线长度之和+K*改变运输路线的次数。
SAMPLE
INPUT
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
OUTPUT
32
//前三天走1-4-5,后两天走1-3-5,这样总成本为(2+2)*3+(3+2)*2+10=32
解题报告
神$DP$= =
首先我们观察,码头有天数的限制,所以显然不能瞎XX直接求最短路(这不是废话吗),所以我们考虑$DP$
我们可以求出在每一段区间内,哪些码头可以用,哪些码头不能用,那么,我们自然可以处理出,每一段区间内,从起点到终点的最小花费(即最短路)。
具体方法:
枚举每一段时间区间,将在这段区间中关闭的码头砍掉,剩下的码头跑单源最短路($SPFA$就可以了)
然后,我们得出了一个数组$cost[i][j]$表示从第$i$天到第$j$天,从起点到终点的最短路,接着就可以写转移方程了。
设$f[i]$表示到第$i$天所需的最小花费:
$$f[i]=min{f[j]+cost[j+1][i]*(i-j)+k}$$
原因很简单,当前状态一定是由上一状态转移而来,我们假设到第$j$天换了航线,并沿此航线航行到第$j$天,这一段所需的花费是$cost[j+1][i]*(i-j)$,换航线所需的花费是$k$,我们只要枚举$i$之前的$j$,求最小值即可转移过来了
需要注意的是,从第$0$天转移过来时,默认换了一次航线,实际上并没有换,所以最后结果要减去一个$k$,即:
$$ans=f[n]-k$$
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 using namespace std; 6 inline int read(){ 7 int sum(0); 8 char ch(getchar()); 9 for(;ch<'0'||ch>'9';ch=getchar()); 10 for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar()); 11 return sum; 12 } 13 struct edge{ 14 int e,w; 15 edge *n; 16 edge():e(0),w(0),n(NULL){} 17 }a[405],eve[405],*pre[25],*nxt[25]; 18 int tot,ttt; 19 inline void insert(int s,int e,int w){ 20 a[++tot].e=e; 21 a[tot].w=w; 22 a[tot].n=pre[s]; 23 pre[s]=&a[tot]; 24 } 25 inline void add(int s,int e,int w){ 26 eve[++ttt].e=e; 27 eve[ttt].w=w; 28 eve[ttt].n=nxt[s]; 29 nxt[s]=&eve[ttt]; 30 } 31 int n,m,k,e,d; 32 bool dat[25][105]; 33 bool can[25]; 34 int cost[105][105],dis[25]; 35 bool vis[25]; 36 inline void spfa(){ 37 memset(dis,0x3f,sizeof(dis)); 38 memset(vis,0,sizeof(vis)); 39 dis[1]=0,vis[1]=1; 40 queue<int>q; 41 q.push(1); 42 while(!q.empty()){ 43 int k(q.front()); 44 vis[k]=0; 45 q.pop(); 46 for(edge *i=nxt[k];i;i=i->n){ 47 int e(i->e); 48 if(dis[e]>dis[k]+i->w){ 49 dis[e]=dis[k]+i->w; 50 if(!vis[e]){ 51 vis[e]=1; 52 q.push(e); 53 } 54 } 55 } 56 } 57 } 58 int f[105]; 59 inline int gg(){ 60 freopen("bzoj_1003.in","r",stdin); 61 freopen("bzoj_1003.out","w",stdout); 62 memset(pre,NULL,sizeof(pre)); 63 n=read(),m=read(),k=read(),e=read(); 64 for(int i=1;i<=e;++i){ 65 int x(read()),y(read()),z(read()); 66 insert(x,y,z),insert(y,x,z); 67 } 68 d=read(); 69 for(int i=1;i<=d;++i){ 70 int x(read()),y(read()),z(read()); 71 for(int j=y;j<=z;++j) 72 dat[x][j]=1; 73 } 74 memset(f,0x3f,sizeof(f)); 75 memset(cost,0x3f,sizeof(cost)); 76 int inf(f[1]); 77 for(int i=1;i<=n;++i){ 78 for(int j=i;j<=n;++j){ 79 memset(can,1,sizeof(can)); 80 memset(nxt,NULL,sizeof(nxt)); 81 ttt=0; 82 for(int o=1;o<=m;++o){ 83 for(int l=i;l<=j;++l) 84 if(dat[o][l]){ 85 can[o]=0; 86 break; 87 } 88 } 89 for(int o=1;o<=m;++o){ 90 if(!can[o]) 91 continue; 92 for(edge *i=pre[o];i;i=i->n){ 93 int e(i->e); 94 if(can[e]) 95 add(o,e,i->w); 96 } 97 } 98 spfa(); 99 cost[i][j]=dis[m]; 100 } 101 } 102 f[0]=0; 103 for(int i=1;i<=n;++i) 104 for(int j=0;j<i;++j) 105 if(f[j]!=inf&&cost[j+1][i]!=inf) 106 f[i]=min(f[i],f[j]+cost[j+1][i]*(i-j)+k); 107 printf("%d",f[n]-k); 108 return 0; 109 } 110 int K(gg()); 111 int main(){;}