[ZJOI2006][线性DP][最短路] 物流运输
\(DP\) + 最短路
外层可以看成是一个线性 \(DP\),就是枚举一下分割点,表示这一个区间内的路线相同,要求总价值最小。
内层我们需要计算一下一个区间的价值,其实就是跑一个最短路就行了。
对于不合法的路线跑最短路时标记出来不使用即可。
代码:
# include <iostream>
# include <cstdio>
# include <queue>
# include <cstring>
# define MAXN 105
# define MAXM 25
# define MAXE 5005
struct edge{
int v, next, w;
}e[MAXE<<1]; int hd[MAXN], cntE;
bool unava[MAXN][MAXM], cant[MAXM];
int dis[MAXM]; bool inQ[MAXM];
int f[MAXN];
void AddE(int u, int v, int w){
e[++cntE] = (edge){v, hd[u], w};
hd[u] = cntE;
}
int SPFA(int from, int to){
memset(dis, 0x3f, sizeof(dis));
std::queue<int>Q;
Q.push(from); dis[from] = 0;
while(Q.size()){
int now = Q.front(); Q.pop();
inQ[now] = 0;
for(int i = hd[now]; i; i = e[i].next){
if(!cant[e[i].v]){
if(dis[e[i].v] > dis[now] + e[i].w){
dis[e[i].v] = dis[now] + e[i].w;
if(!inQ[e[i].v]){
Q.push(e[i].v);
inQ[e[i].v] = 1;
}
}
}
}
}
return dis[to];
}
int main(){
int n, m, K, e, d;
scanf("%d%d%d%d", &n, &m, &K, &e);
for(int i = 1, u, v, w; i <= e; i++){
scanf("%d%d%d", &u, &v, &w);
AddE(u, v, w); AddE(v, u, w);
}
scanf("%d", &d);
for(int i = 1, p, a, b; i <= d; i++){
scanf("%d%d%d", &p, &a, &b);
for(int j= a; j <= b; j++){
unava[j][p] = 1;
}
}
memset(f, 0x3f, sizeof(f));
f[0] = -K; // 细节,第一次选择的路线不算修改的路线
for(int i = 1; i <= n; i++){
memset(cant, 0, sizeof(cant));
for(int j = i, tmp; j >= 1; j--){
for(int k = 1; k <= m; k++){
if(unava[j][k]){
cant[k] = 1;
}
}
tmp = SPFA(1, m);
// printf("%d\n", tmp);
if(tmp == 0x3f3f3f3f){
break;
}
f[i] = std::min(f[i], f[j-1]+(i-j+1)*tmp+K);
}
}
printf("%d", f[n]);
return 0;
}