[ZJOI2006]物流运输

Posted by Panda2134's Blog on November 8, 2017

链接:Luogu-P1772

分析

在输入时对每个码头的不可用时间进行差分。假设某一段连续的时间$[t_1,t_2]$选择同一条运输路线,则可以通过Dijkstra求出这段时间的$s \rightarrow t$最短路长度,记作$cost[t_1][t_2]$。总时间是$t$,点数目是$n$,那么预处理每个时间段内选择同一条路的代价的时间复杂度是$O(n) \cdot O(t^2) \cdot O(n \log n) = O(n^2 t^2 \log n)$。显然是可以承受的。
我们再来考虑一段时间内是用同一条路径更优,还是改变路线更优。注意到总代价=运输路线长度和+K$\cdot$改变路线次数。也就是说总代价和改变次数成线性关系。而时间又是个有序的量,就可以考虑进行DP。 状态为$f(i,j):$在时间段$[i,j]$的最小总代价。 于是就可以转移了: $f(i,j)=\min \begin{cases} cost[i][j], \ f(i,p)+f(p,j)+K \; (p=i,i+1, \dots , j-1) \end{cases}$ 复杂度是$O(t^3)$的,也同样可以承受。

代码

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
struct Edge {
    int v, len, next;
};


const int MAXT = 100, MAXN = 20, MAXM = 400, INF = 0x3f3f3f3f;
int T, N, K, M, e_ptr, head[MAXN+10], dist[MAXN+10], dif[MAXN+10][MAXT+10], 
    cost[MAXT+10][MAXT+10], opt[MAXT+10][MAXT+10];
bool done[MAXN+10], del[MAXN+10];  Edge E[(MAXM<<1)+10];


void AddEdge(int u, int v, int len) {
    E[++e_ptr]=(Edge){v,len,head[u]}; head[u]=e_ptr;
}
void AddPair(int u, int v, int len) {
    AddEdge(u,v,len); AddEdge(v,u,len);
}


void Dijkstra() {
    priority_queue<pii, vector<pii>, greater<pii> > pq;
    memset(dist, 0x3f, sizeof(dist));
    memset(done, 0, sizeof(done));
    
    if(del[1]) return;
    
    pq.push(make_pair(0,1)); dist[1]=0;
    while(!pq.empty()) {
        pii cur=pq.top(); pq.pop();
        int u=cur.second;
        if(done[u]) continue;
        done[u]=true;
        for(int j=head[u];j;j=E[j].next) {
            int v=E[j].v, len=E[j].len;
            if(!del[v] && dist[v] > dist[u] + len) {
                dist[v] = dist[u] + len;
                pq.push(make_pair(dist[v],v));
            }
        }
    }
}
void Init() {
    int u,v,c,p,a,b,d;
    scanf("%d%d%d%d", &T, &N, &K, &M);
    for(int i=1; i<=M; i++) {
        scanf("%d%d%d", &u, &v, &c);
        AddPair(u,v,c);
    }
    scanf("%d", &d);
    while(d--) {
        scanf("%d%d%d", &p, &a, &b);
        dif[p][a]++; dif[p][b+1]--;
    }
    
    //差分数组+前缀和 
    for(int u=1; u<=N; u++) {
        for(int i=1; i<=T; i++)
            dif[u][i]+=dif[u][i-1];
        for(int i=1; i<=T; i++)
            dif[u][i]+=dif[u][i-1];
    }
    
    //Dijkstra
    for(int t1=1; t1<=T; t1++)
        for(int t2=t1; t2<=T; t2++) {
            memset(del, 0, sizeof(del));
            for(int u=1; u<=N; u++)
                if(dif[u][t2]-dif[u][t1-1] != 0) { //disabled
                    del[u]=true;
                }
            Dijkstra();
            cost[t1][t2]=dist[N];
        }
}
void Work() {
    //DP
    //len=1 边界
    for(int len=1; len<=T; len++)
        for(int i=1; i+len-1<=T; i++) {
            int j=i+len-1;
            if(cost[i][j] != INF) //注意防止溢出!
                opt[i][j]=cost[i][j]*len; //一条路径代价*天数 
            else opt[i][j]=INF;
            for(int p=i; p<=j-1; p++)
                opt[i][j] = min(opt[i][j], opt[i][p] + opt[p+1][j] + K);
        } 
    printf("%d", opt[1][T]);
}
int main() {
    Init(); Work();
    return 0;
}