【luogu】P1772物流运输(最短路+DP)

  题目链接

  对于本题我们设ext[i][j]计算第i个码头在前j天总共有几天不能用(其实就一前缀和),设dis[i][j]是从第i天到第j天不变运输路线的最短路径,设f[i]是前i天运输货物的最小花费。

  然后n2*O(spfa)处理出整个dis数组。判断一个码头a从第i天到第j天能不能用的方法是ext[a][j]-ext[a][i-1],如果该数等于0,则说明这几天没有一天不能用,则spfa的时候可以扩展到该码头;否则至少有一天不能用,spfa的时候也就不能扩展。

  接着f[i]=min(f[i],f[j]+dis[j+1][i]*(i-j)+k)  其中i-j是天数,j是枚举的从哪天开始换路线。

  最后f[n]即是答案。

  放上代码

  

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<cctype>
#include<algorithm>

inline int read(){
    int num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

using namespace std;
queue <int>    q;
bool vis[100200];
int dst[1500];
int dis[150][150];
int n,m,o,e;

int ext[30][150];

struct Edge{
    int next,to,val;
}edge[1000200];
int head[12000],num;
inline void add(int from,int to,int val){
    edge[++num]=(Edge){head[from],to,val};
    head[from]=num;
}

void Spfa(int s,int t){
    q.push(1);memset(dst,127/3,sizeof(dst));dst[1]=0;
    while(!q.empty()){
        int from=q.front();    q.pop();vis[from]=0;
        for(int i=head[from];i;i=edge[i].next){
            int to=edge[i].to;
            if(ext[to][t]-ext[to][s-1]>0)    continue;
            if(dst[to]>dst[from]+edge[i].val){
                dst[to]=dst[from]+edge[i].val;
                if(vis[to])    continue;
                q.push(to);    vis[to]=1;
            }
        }
    }
    dis[s][t]=dst[m];
}

long long f[2000];

int main(){
    memset(f,127/3,sizeof(f));memset(dis,127/3,sizeof(dis));
    n=read(),m=read(),o=read(),e=read();
    f[0]=-o;
    for(int i=1;i<=e;++i){
        int from=read(),to=read(),val=read();
        add(from,to,val);
        add(to,from,val);
    }
    int T=read();
    for(int i=1;i<=T;++i){
        int x=read(),a=read(),b=read();
        for(int j=a;j<=b;++j)    ext[x][j]=1;
    }
    for(int i=1;i<=m;++i)
        for(int j=1;j<=n;++j)    ext[i][j]+=ext[i][j-1];
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            Spfa(i,j);
    for(int i=1;i<=n;++i)
        for(int j=0;j<i;++j){
            if(dis[j+1][i]==dis[0][0])    continue;
            f[i]=min(f[i],f[j]+dis[j+1][i]*(i-j)+o);
        }
    printf("%d",f[n]);
    return 0;
}
原文地址:https://www.cnblogs.com/cellular-automaton/p/7891281.html