[ZJOI2006] 物流运输

[ZJOI2006] 物流运输

题目大意:连续\(n\)天在一个图上走最短路,但是每个点有一定时间不开放,改变路线要加大花费,在图上走也需要花费,求最小花费。

Solution

计算出某天到某天的最短路,进行\(dp\)

  • 状态\(f[i]\)表示到第\(i\)天的最小花费
  • 转移方程\(f[i] = min\{dis[1][i] \cdot i, f[j - 1] + dis[j][i] \cdot (i - j + 1) + k (1<j \leq i)\}\)

Code

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

const int Maxm = 25;
const int Maxn = 105;
const int INF = 1e9;

struct Edge{
    int to, next, len;
}e[20005];

int n, m, qwq, k, d, cnt, head[Maxm], ban[Maxm][Maxn], vis[Maxm], f[Maxn], dist[Maxn][Maxn], dis[Maxm];

inline void addedge(int x, int y, int z){
    ++cnt;
    e[cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].len = z;
    head[x] = cnt;
}

inline int SPFA(int x, int y){
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    std::queue <int> q;
    q.push(1);
    dis[1] = 0;
    vis[1] = 1;
    int u, v;
    for(int i = 1; i <= m; ++i){
        for(int j = x; j <= y; ++j){
            if(ban[i][j]){
                vis[i] = 1;
                break;
            }
        } 
    } 
    while(!q.empty()){
        
        u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].next){
            v = e[i].to;
            if(dis[v] > dis[u] + e[i].len){
                dis[v] = dis[u] + e[i].len;
                if(!vis[v]){
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return dis[m];
}

int main(){
    scanf("%d %d %d %d", &n, &m, &k, &qwq);
    for(int i = 1, la, lb, lc; i <= qwq; ++i){
        scanf("%d %d %d", &la, &lb, &lc);
        addedge(la, lb, lc);
        addedge(lb, la, lc);
    }//输入连接的边
    scanf("%d", &d);
    for(int i = 1, la, lb, lc; i <= d; ++i){
        scanf("%d %d %d", &la, &lb, &lc);
        for(int j = lb; j <= lc; ++j){
            ban[la][j] = 1;
        }
    }//ban表示第la个码头第j天是否被禁止运输了

    for(int i = 1; i <= n; ++i)
        for(int j = i; j <= n; ++j){
            dist[i][j] = SPFA(i, j);
        }//计算某一天到某一天的最短距离

    for(int i = 1; i <= n; ++i){
        if(dist[1][i] < 1061109567)
            f[i] = dist[1][i] * i;//不改变航线
        else 
            f[i] = INF; 
        for(int j = 1; j <= i; ++j){
            if(dist[j][i] < 1061109567)
            f[i] = std::min(f[i], f[j - 1] + dist[j][i] * (i - j + 1) + k);
        }//枚举上一次改变航线的日期
    }
    printf("%d\n", f[n]);
    return 0;
}
posted @ 2018-09-03 17:03  LMSH7  阅读(108)  评论(0编辑  收藏  举报