[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;
}
posted @ 2020-09-24 19:25  ChPu437  阅读(100)  评论(0编辑  收藏  举报