题目描述
物流公司要把一批货物从码头A运到码头B。由于货物量比较大,需要n天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。但是修改路线是—件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个n天的运输计划,使得总成本尽可能地小。
(1≤n≤100)、m(1≤m≤20)
(
1
≤
n
≤
100
)
、
m
(
1
≤
m
≤
20
)
题意:
给你一张图要求你走n次的从1到m的路径,每次有一些点不可走,相邻两次若路径不同则有额外代价,求最小代价。
题解
看到 m 只有 20 ,可以想到状压,但本人太弱不知道如何很好的实现。
于是我们就来普通的dp吧。
这里一个很烦人的点就是每次都有点不能走,给我们判断最优解带来很大困扰,所以状态的设计至关重要。
假设我们一天天来看,我们不知道今天这条路径应该怎样走,因为合并两个相邻天的答案很困难,且由于可能有多种不同的最短路存在,我们不好判断最优解。
所以我们应该将一段天看成一个整体来跑最短路。所以很容易设出以下状态:
设dp[i][j]表示第i天到第j天跑同一条最短路所需的最小代价
设
d
p
[
i
]
[
j
]
表
示
第
i
天
到
第
j
天
跑
同
一
条
最
短
路
所
需
的
最
小
代
价
转移的话再在前面取一天进行转移就行了,具体可以看代码。
时间复杂度应该是 O(n3) O ( n 3 ) ,但不满。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#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天也看成跑了一次不同的路径。
}