2018.09.02 bzoj1003: [ZJOI2006]物流运输(dp+最短路转移)

时间:2022-09-20 05:29:49

传送门

dp好题。

每一天要变更路线一定还是走最短路。

所以l~r天不变更路线的最优方案就是把l~r天所有不能走的点都删掉再求最短路。显然是可以dp的。

设f[i]表示第i天的最优花销。那么我们枚举在哪里切换路线更优,则有状态转移方程:

f[i]=min(f[j]+spfa(j,i)∗(i−j)+k)(j=1...i−1)" role="presentation" style="position: relative;">f[i]=min(f[j]+spfa(j,i)∗(i−j)+k)(j=1...i−1)f[i]=min(f[j]+spfa(j,i)∗(i−j)+k)(j=1...i−1)

其中spfa时只有合法的点参与。

另外可以bitset优化一波。

代码:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int day,n,K,E,f[105],cnt=0,first[30],d[30];
struct edge{int v,w,next;}e[20000];
bool in[30];
bitset<21>del[105];
inline void add(int u,int v,int w){e[++cnt].v=v,e[cnt].next=first[u],e[cnt].w=w,first[u]=cnt;}
inline int spfa(int l,int r){
    queue<int>q;
    bitset<21>dell;
    for(int i=l;i<=r;++i)dell|=del[i];
    for(int i=1;i<=n;++i)in[i]=false,d[i]=inf;
    q.push(1),d[1]=0,in[1]=true;
    while(!q.empty()){
        int x=q.front();
        q.pop(),in[x]=false;
        for(int i=first[x];i;i=e[i].next){
            int v=e[i].v;
            if(dell[v])continue;
            if(d[v]>d[x]+e[i].w){
                d[v]=d[x]+e[i].w;
                if(!in[v])in[v]=true,q.push(v);
            }
        }
    }
    return d[n];
}
int main(){
    day=read(),n=read(),K=read(),E=read();
    for(int i=1;i<=E;++i){
        int a=read(),b=read(),c=read();
        add(a,b,c),add(b,a,c);
    }
    int d=read();
    while(d--){
        int tmp=read(),a=read(),b=read();
        for(int i=a;i<=b;++i)del[i][tmp]=1;
    }
    f[1]=spfa(1,1);
    for(int i=2;i<=day;++i){
        int tmp=spfa(1,i);
        if(tmp!=inf)f[i]=tmp*i;
        else f[i]=inf;
        for(int j=1;j<i;++j){
            tmp=spfa(j+1,i);
            if(tmp!=inf)f[i]=min(f[i],f[j]+K+(i-j)*tmp);
        }
    }
    cout<<f[day];
    return 0;
}