BZOJ1003: [ZJOI2006]物流运输

题目链接

发现如果没有限制的话,就是最短路的模板题。但是这道题的关键就是要处理题上的限制。我们就可以用一个数组来存哪一天哪个港口不能走,跑最短路的时候特判一下,dijkstra处理出所有天的最短路径。

由于数据范围n<=100,所以n^2*(elogm)的时间复杂度也随便能跑。

接下来又怎么办呢?就可以考虑dp,看了题解发现dp的思路还是比较简单的。考虑如何设计状态,dp[i]表示在第i天的最小费用,而我们还要枚举j,也就是在第i天之前是否改变了路径。

可以得出转移方程dp[i]=min(dp[i],dp[j]+k+(i-j)*cost[j+1][i])

代码如下:

 

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1025;
const int N=550;
priority_queue< pair<long long,int> > q;
long long dis[maxn];
struct node{
    int nxt,to,val;
}edge[maxn*3];
bool vis[maxn];
int head[maxn],cnt;
int x,y,v;
void add(int x,int y,int v){
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;
    edge[cnt].val=v;
    head[x]=cnt;
}
int used[N][N]; 
int lock[maxn];
int cost[N][N];
long long dp[maxn];
long long min(long long a,long long b){
    return a>b?b:a;
}
int n,m,k,e;
int d,p,a,b;
long long dijkstra(int x,int y){
    memset(vis,false,sizeof(vis));
    memset(dis,88,sizeof(dis));
    memset(lock,false,sizeof(lock));
    for(int i=1;i<=m;i++){
        for(int j=x;j<=y;j++) if(used[i][j]) lock[i]=true;
    }
    dis[1]=0;
    q.push(make_pair(0,1));
    while(!q.empty()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=true;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(dis[v]>dis[u]+edge[i].val&&(!lock[v])){
                dis[v]=dis[u]+edge[i].val;
                q.push(make_pair(-dis[v],v));
            }
        }
    }
    return dis[m];
}
int main(){
    scanf("%d%d%d%d",&n,&m,&k,&e);
    for(int i=1;i<=e;i++){
        scanf("%d%d%d",&x,&y,&v);
        add(x,y,v);add(y,x,v);
    }
    scanf("%d",&d);
    for(int i=1;i<=d;i++){
        scanf("%d%d%d",&p,&a,&b);
        for(int j=a;j<=b;j++) used[p][j]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++) cost[i][j]=dijkstra(i,j);
    } 
    for(int i=1;i<=n;i++){
        dp[i]=(long long)i*cost[1][i];
        for(int j=1;j<=i;j++){//在第j天更换 
            dp[i]=min(dp[i],dp[j]+k+(long long)(i-j)*cost[j+1][i]); 
        }
    }
    printf("%lld\n",dp[n]);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/LJB666/p/11502486.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值