BZOJ1003 物流运输 (DP spfa)

题目大意

给出m个点e条带权边,在n天内每天从1到n走一遍,边权即为代价,每次更换线路需要k点代价,问最小的代价。


题解

考虑到数据范围很小,可以先预处理出所有时间区间内的从1到n的最短路的长度dis[i][j],然后进行dp。

初始状态: f[i][j]=dis[i][j]
dp方程为: f[i][j]=min(f[i][k-1]+f[k][j]+K) (i

代码:

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

const int maxn=25, maxl=105;
int tim,n,K,m;
bool occ[maxl][maxl][maxn];
int tot=0,head[maxn];

struct Edge {
    int from,to,val,next;
    Edge() {}
    Edge(int x,int y,int v,int nx):from(x),to(y),val(v),next(nx) {}
}eage[maxn*maxn];
void add(int x,int y,int val) {
    eage[tot]=Edge(x,y,val,head[x]), head[x]=tot++;
    eage[tot]=Edge(y,x,val,head[y]), head[y]=tot++;
    return;
}

const long long INF=(1ll<<52);
long long dis[maxl][maxl][maxn];
bool used[maxn];
queue<int> que;

void spfa(long long *dis,bool *occ) {
    for(int i=1;i<=n;i++) dis[i]=INF, used[i]=false;
    while(que.size()) que.pop();
    dis[1]=0;
    used[1]=true;
    que.push(1);
    while(que.size()) {
        int u=que.front(); que.pop();
        used[u]=false;
        for(int i=head[u];~i;i=eage[i].next) if(dis[eage[i].to]>dis[u]+eage[i].val) {
            int v=eage[i].to;
            if(occ[v]) continue;
            dis[v]=dis[u]+eage[i].val;
            if(!used[v]) {used[v]=true; que.push(v);}
        }
    }
    return;
}

long long f[maxn][maxn];

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    scanf("%d%d%d%d",&tim,&n,&K,&m);
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=m;i++) {
        int x,y,val;
        scanf("%d%d%d",&x,&y,&val);
        add(x,y,val);
    }

    int p;
    scanf("%d",&p);
    for(int t=0;t<p;t++) {
        int id,l,r;
        scanf("%d%d%d",&id,&l,&r);
        for(int i=1;i<=tim;i++)
            for(int j=i;j<=tim;j++) if((l>=i && l<=j) || (r>=i && r<=j))
                occ[i][j][id]=true;
    }
    for(int i=1;i<=tim;i++)
        for(int j=i;j<=tim;j++)
            spfa(dis[i][j],occ[i][j]);

    for(int i=1;i<=tim;i++)
        f[i][i]=dis[i][i][n];

    for(int len=2;len<=tim;len++)
    for(int i=1;i+len-1<=tim;i++) {
        int j=i+len-1;
        f[i][j]=dis[i][j][n]*len;
        for(int k=i+1;k<=j;k++) f[i][j]=min(f[i][j],f[i][k-1]+f[k][j]+K);
    }
    printf("%lld\n",f[1][tim]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值