[ZJOI2006]物流运输 luogu.P1772

今天特别高兴,所以写博客2333

一道浙江省选题
SPFA+DP

可能正常的思维是:
我们可以求出一段最短路让这段路在某一段时间内可以一直使用
然后我们如果求出来每一段时间内的这条路就很容易知道答案
然后我们就可以DP
接下来就是后面的内容了。(我直接把我在洛谷发的题解粘过来了)
相信自己可以看得懂233

//[ZJOI2006]物流运输  luogu.P1772 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
/*题解:
    动规g[i][j]表示i到j天一直用同一条最短路的最小费用 
        即i到j天不改路线的最短路
        spfa(i,j)求g[i][j] 
        至于某个点在这段时间内是不是被更改过的判断
        看下面的sum数组,真是奇妙判断法。。 
    动规f[i]表示从第1天到第i天的最小花费 
        =min(f[i],f[j]+g[j+1][i]*(i-j)+K)
 */
inline int read(){
    char ch=getchar();int num=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
        num*=10,num+=ch-'0',ch=getchar();
    return num;
}
const int N=105,M=25;
const int E=10005;
/*  
    The game played by G.S.M. && Goes
    It has started
    Come on , boy!
*/
struct ss{
    int to,nex,va;
}edge[E];
int head[M],ecnt,sum[M][N];
void add(int va,int x,int y){
    edge[++ecnt]=(ss){y,head[x],va};
    edge[++ecnt]=(ss){x,head[y],va};
    head[x]=ecnt-1;head[y]=ecnt;
}void mark(int e,int s,int pos){
    for(int i=s;i<=e;i++)
        sum[pos][i]=1;
}
int n,m,K,e,d;
int g[N][N],f[N];
inline void gsin(){
    n=read(),m=read(),K=read(),e=read();
    for(int i=1;i<=e;i++)
        add(read(),read(),read());
    for(int i=1;i<=n;i++) f[i]=9999999;
    //奇妙标记法 
    d=read();//sum[pos][r]-sum[pos][l]!=0说明不可以
    for(int i=1;i<=d;i++) 
        mark(read(),read(),read());
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            sum[i][j]+=sum[i][j-1];
}
inline void gsout(){
    printf("%d",f[n]-K);
}
int dis[M],vis[M];
int spfa(int l,int r)
{
    for(int i=1;i<=m;i++) dis[i]=999999,vis[i]=0;
    queue<int> q;
    q.push(1);dis[1]=0;vis[1]=1;
    while(!q.empty()){
        int sn=q.front();q.pop();vis[sn]=0;
        for(int i=head[sn];i;i=edge[i].nex){
            int fn=edge[i].to,val=edge[i].va;
            if(sum[fn][r]-sum[fn][l-1]||
                dis[fn]<=dis[sn]+val) continue;

            dis[fn]=dis[sn]+val;
            if(!vis[fn]){
                vis[fn]=1;
                q.push(fn); 
            }
        }
    }
    return dis[m];
}

inline void gsdp(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            g[i][j]=spfa(i,j);
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
            f[i]=min(f[i],f[j]+g[j+1][i]*(i-j)+K);
}
int main()
{
    gsin();
    gsdp();
    gsout();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoesM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值