P1772 [ZJOI2006]物流运输

传送门

预处理 dis [ i ] [ j ] 表示从第 i 天到第 j 天不改变路线的最短路径

然后就可以愉快地推方程了

设 f [ i ] 表示从第一天到第 i 天的最少花费

那么 f [ j ] = min(f [ j ] , f [ i ] + dis[ i+1 ] [ j ] * (j-i) + K)

预处理的用的是 Dijkstra

注意最后改变航线的次数比我们方程里+K的次数少 1,所以最后要减 1

(我的方程初始的航线也算改变,然而初始不算改变)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1007,M=27;
int fir[M],from[N],to[N],val[N],cnt;
inline void add(int &a,int &b,int &c)
{
    from[++cnt]=fir[a];
    fir[a]=cnt; to[cnt]=b; val[cnt]=c;
}
int n,m,e,d;
int l[N],r[N],id[N];//存关闭港口的信息,从第l[i]到第r[i]天,id[i]号港口关闭
int dis[107][107][M];
bool p[M];//判断此时港口是否关闭
struct node
{
    int pos,dis;
    inline bool operator < (const node &tmp) const {
        return dis>tmp.dis;
    }
};//Dijkstra的结构体
priority_queue <node> q;
void Dijk(int L,int R)//求从第L天到第R天不改变路线的最短路径
{
    memset(p,0,sizeof(p));
    for(int i=1;i<=d;i++)
    {
        if(r[i]<L||l[i]>R) continue;
        p[id[i]]=1;//只要时间段有相交的区间就不能走
    }
    q.push((node){1,0}); dis[L][R][1]=0;//愉快地跑Dijkstra
    while(!q.empty())
    {
        node x=q.top(); q.pop();
        if(x.dis!=dis[L][R][x.pos]) continue;
        for(int i=fir[x.pos];i;i=from[i])
        {
            int &v=to[i]; if(p[v]) continue;
            if(dis[L][R][v]>dis[L][R][x.pos]+val[i])
            {
                dis[L][R][v]=dis[L][R][x.pos]+val[i];
                q.push((node){v,dis[L][R][v]});
            }
        }
    }
}
int K,f[N];
int main()
{
    memset(dis,127,sizeof(dis));
    memset(f,127,sizeof(f)); f[0]=0;//注意初始化
    int a,b,c;
    n=read(); m=read(); K=read(); e=read();
    for(int i=1;i<=e;i++)
    {
        a=read(),b=read(),c=read();
        add(a,b,c); add(b,a,c);
    }
    d=read();
    for(int i=1;i<=d;i++) id[i]=read(),l[i]=read(),r[i]=read();
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) Dijk(i,j);//预处理
    for(int i=0;i<n;i++)
        for(int j=i+1;j<=n;j++)
            if(dis[i+1][j][m]<2e9)//注意如果太大说明无路可走就不用考虑,不然后面乘的时候可能爆成负数
                f[j]=min(f[j],f[i]+dis[i+1][j][m]*(j-i)+K);
    printf("%d",f[n]-K);//注意最后改变次数少一
    return 0;
}

 

posted @ 2018-10-29 10:40  LLTYYC  阅读(118)  评论(0编辑  收藏  举报