Problem Specification Value
Time Limit 10 Sec
Memory Limit 162 MB
Submit 5868
Solved 2424

Description

物流公司要把一批货物从码头 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

Sample Output

32

HINT

前三天走 1-4-5, 后两天走 1-3-5, 这样总成本为(2+2)*3+(3+2)*2+10=32

Source


SPFA 最短路+动规.

DP 转移方程有这样: 记 cost[i][j] 为第 i 天到第 j 天都走同一路线的费用, 再记 f[i] 为从第一天到第 n 天的花费最小值, 然后就有转移方程:f[i] = min{f[j] + cost[j + 1][i] * (i - j) + k} for j in range[1, i - 1] or min{cost[1][i]}.

话说题目数据很坑 233, 因为 maxnmaxmmaxe 的差别看了我好长时间才用 gdb 调出结果, 发现数组开的太小太小了~ SPFA 直接SIGSEGV 啊 2333



#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;
const int maxn = 210, maxm = 25, maxe = 1000;
const long long infinit = 0x3f3f3f3f3f3f3f3fll;

class SPFA
{
public:
    struct edge { int u, v, len; edge* next; };
    edge        *edges[maxm], epool[maxe];
    int         ecnt, inque[maxm], n, s, t;
    long long   dist[maxm];
    queue<int>  que;
    void addedge(int u, int v, int len)
    {
        edge *p = &epool[++ecnt], *q = &epool[++ecnt];
        p->u = u; p->v = v; p->len = len; p->next = edges[u]; edges[u] = p;
        q->u = v; q->v = u; q->len = len; q->next = edges[v]; edges[v] = q;
        return ;
    }
    void clear(void)
    {
        ecnt = 0;
        for (int i = 1; i <= n; i++)
            edges[i] = NULL, dist[i] = infinit;
        memset(epool, 0, sizeof(epool));
        memset(inque, 0, sizeof(inque));
        return ;
    }
    void process(void)
    {
        while (!que.empty()) que.pop();
        for (int i = 1; i <= n; i++) dist[i] = infinit;
        dist[s] = 0; inque[s] = true; que.push(s);
        while (!que.empty()) {
            int p = que.front(); que.pop();
            for (edge *ep = edges[p]; ep; ep = ep->next) {
                if (dist[p] >= infinit);
                if (dist[ep->v] > dist[p] + ep->len) {
                    dist[ep->v] = dist[p] + ep->len;
                    if (!inque[ep->v]) {
                        inque[ep->v] = true;
                        que.push(ep->v);
                    }
                }
            }
            inque[p] = false;
        }
        return ;
    }
    void dumpme()
    {
        for (int i = 1; i <= n; i++) {
            for (edge *ep = edges[i]; ep; ep = ep->next)
                printf("dumping %d -> %d : %d\n", ep->u, ep->v, ep->len);
        }
        return ;
    }
} graph;

int         n, m, K, e, P;
int         avail[maxm][maxn][maxn], prevail[maxm][maxn];
int         edge_data[maxe][3];
long long   cost[maxn][maxn], f[maxn];

int main(int argc, char** argv)
{
    // Read basic graph from input
    scanf("%d%d%d%d", &n, &m, &K, &e);
    for (int i = 1; i <= e; i++)
        scanf("%d%d%d", &edge_data[i][0], &edge_data[i][1], &edge_data[i][2]);
    // Reset 'available' array to 1
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            prevail[i][j] = 1;
    // Read unavailable data from input
    scanf("%d", &P);
    for (int i = 1, a, b, c; i <= P; i++) {
        scanf("%d%d%d", &a, &b, &c);
        for (int j = b; j <= c; j++)
            prevail[a][j] = 0;
    }
    // Preprocess available data
    memset(avail, 0, sizeof(avail));
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++) {
            avail[i][j][j] = prevail[i][j];
            for (int k = j + 1; k <= n; k++) {
                avail[i][j][k] = avail[i][j][k - 1] && prevail[i][k];
                if (!avail[i][j][k])
                    break;
            }
        }
    // Build graph for n * n times
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++) {
            graph.n = m; graph.s = 1; graph.t = m;
            graph.clear();
            for (int k = 1; k <= e; k++)
                if (avail[edge_data[k][0]][i][j] && avail[edge_data[k][1]][i][j])
                    graph.addedge(edge_data[k][0], edge_data[k][1], edge_data[k][2]);
            graph.process();
            cost[i][j] = graph.dist[graph.n];
        }
    // Dynamic programming resolves the result
    for (int i = 1; i <= n; i++) {
        f[i] = cost[1][i] < infinit ? cost[1][i] * i : infinit;
        for (int j = 1; j <= i - 1; j++)
            if (f[j] < infinit && cost[j + 1][i] < infinit)
                f[i] = min(f[i], f[j] + cost[j + 1][i] * (i - j) + K);
    }
    long long res = f[n];
    printf("%lld\n", res);
    return 0;
}

# Sometimes generate impossible data, beware.
from pydatagen import *
n = randrange(1, 100)
m = randrange(3, 20)
K = randrange(10, 1000)
e = randrange(m, m * (m - 1) / 2 + 1)
printf('%d %d %d %d\n' % (n, m, K, e))
G = Graph(m, e, connected=True, weighed=True, weight_range=range(1, 10))
print_oi(G)
d = randrange(1, m * m / 4)
printf('%d\n' % d)
for i in range(0, d):
    tmp = randlist(2, range(1, n + 1), distinct=True)
    if tmp[1] < tmp[0]: tmp[0], tmp[1] = tmp[1], tmp[0]
    printf('%d %d %d\n' % (randrange(1, m + 1), tmp[0], tmp[1]))
fclose()