洛谷 P1772 [ZJOI2006]物流运输

调试了好久的题..原因是因为题目里没有给\(e\)的取值范围,我凭借主观臆断把范围写成了\(200\),但其实通过理性分析可以分析出\(e\)的范围最大应该是在这一张图是完全图时,为\(400\)...

题目描述:
P1772 [ZJOI2006]物流运输

题目分析
此题将\(DP\)和最短路结合到了一起,是一道不错的题目.
先令\(DP[i]\)来表示前\(i\)天所需要的最小花费,那么可以想出这样一个转移方程

\[DP[i]=min(DP[j]+(i-j)*con[j+1][i]+k) j\in{[0,i-1]} \]

解释一下这个方程,就是我们选择在第\(j+1\)天时切换路线,并且在\(j+1\)\(i\)天都走这一条路线,然后加上切换路线所用的\(k\)
那么如何保证中间走的每一条路线都是当前状况下最优的呢?就可以想到最短路算法了.
对于\(x\)\(y\)天,我们先预处理一下在这段时间里哪些点是不能走的,然后跑一边最短路,将结果存在\(con[x][y]\)里面,来表示\(x\)\(y\)天里都走一条路径的情况下,这条路径的最小值.
\(DP\)过程中应将初值赋为\(con[1][i]*i\).

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int M = 410, INF = 1e8;

int n, m, k, E, d;
int h[M], e[M], w[M], ne[M], idx, dis[M];
bool judge[M][M], vis[M], cant[M];  //judge[i][j]表示i码头在第j天能不能走
LL dp[M]; //dp[i]表示前i天的最小花费
LL con[M][M];
//con[i][j]表示第i天到第j天都走同一条最短路的花费

void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void spfa() {
	
    for (int i = 1; i <= m; i++)
        dis[i] = INF, vis[i] = false; 

    queue<int> q;
    dis[1] = 0;
    vis[1] = true;
    q.push(1);

    while (!q.empty()) {
        int t = q.front();
        q.pop();
        vis[t] = false;
		
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (cant[j]) continue;

            if (dis[j] > dis[t] + w[i]) {
                dis[j] = dis[t] + w[i];
                if (!vis[j]) {
                	vis[j] = true;
                	q.push(j);
				}
            }
        }
    }
}

int main() {
    scanf("%d%d%d%d", &n, &m, &k, &E);
    memset(h, -1, sizeof(h));

    for (int i = 1; i <= E; i++) {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        add(a, b, w); add(b, a, w);
    }

    scanf("%d", &d);

    for (int i = 1; i <= d; i++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);

        for (int j = b; j <= c; j++)
            judge[a][j] = 1;
    }
	
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            memset(cant, false, sizeof(cant));

            for (int l = i; l <= j; l++)
                for (int r = 1; r <= m; r++)
                    if (judge[r][l])
                        cant[r] = 1;
		 
            spfa();
	    con[i][j] = dis[m];
        }

    memset(dp, 0x7f, sizeof(dp));

    for (int i = 1; i <= n; i++) {
        dp[i] = con[1][i] * i;
		
        for (int j = i - 1; j >= 0; j--) {
            dp[i] = min(dp[i], dp[j] + con[j + 1][i] * (i - j) + k);
        }
    }

    printf("%lld\n", dp[n]);
}
posted @ 2020-11-29 08:43  lew2018  阅读(77)  评论(0)    收藏  举报
Live2D