【JZOJ3430】DY引擎

40 篇文章 0 订阅
8 篇文章 0 订阅

Description

BOSS送给小唐一辆车。小唐开着这辆车从PKU出发去ZJU上课了。

众所周知,天朝公路的收费站超多的。经过观察地图,小唐发现从PKU出发到ZJU的所有路径只会有N(2<=N<=300)个不同的中转点,其中有M(max(0, N-100) <=M<=N)个点是天朝的收费站。N个中转点标号为1…N,其中1代表PKU,N代表ZJU。中转点之间总共有E(E<=50,000)条双向边连接。

每个点还有一个附加属性,用0/1标记,0代表普通中转点,1代表收费站。当然,天朝的地图上面是不会直接告诉你第i个点是普通中转点还是收费站的。地图上有P(1<=P<=3,000)个提示,用[u, v, t]表示:[u, v]区间的所有中转点中,至少有t个收费站。数据保证由所有标记得到的每个点的属性是唯一的。

车既然是BOSS送的,自然非比寻常了。车子使用了世界上最先进的DaxiaYayamao引擎,简称DY引擎。DY引擎可以让车子从U瞬间转移到V,只要U和V的距离不超过L(1<=L<=1,000,000),并且U和V之间不能有收费站(小唐良民一枚,所以要是经过收费站就会停下来交完钱再走)。

DY引擎果然是好东西,但是可惜引擎最多只能用K(0<=K<=30)次。


Input

第一行有6个整数N,M,E,P,L,K分别代表:N个中转点,M个收费站,E条边,P个提示,DY引擎的有效距离L,DY引擎的使用次数K。

接下去E行,每行有3个整数u,v,w(1<=u, v<=N; 1<=w<=1,000,000)表示:u和v之间有一条长度为w的双向边。

接下去P行,每行有3个整数u,v,t(1<=u<=v<=N; 0<=t<=u-v+1)表示: [u, v] 标号区间至少有t个收费站。

Output

输出一个整数,表示小唐从PZU开到ZJU用的最短距离(瞬间转移距离当然是按0来计算的)。


Sample Input

6 2 6 2 5 1
1 2 1
2 3 2
3 6 3
1 4 1
4 5 2
5 6 3
2 5 2
4 6 2

Sample Output

1

【样例解释】
4、5是收费站。1->2(1)->6(1)


Data Constraint

对于30%的数据保证:
2<=N<=30,max(0, N-10) <=M<=N,0<=k<=10

对于100%的数据保证:
2<=N<=300,max(0, N-100) <=M<=N,E<=50,000,1<=P<=3,000,1<=L<=1,000,000,0<=K<=30


Solution

首先分析一下题目,首先收费站和别的问题是完全分开的,所以我们先求解收费站。

Si 表示 1 ~i有多少个收费站,那么原题可转化成:

有n个变量,P个形如 SiSjk 的条件约束,满足只有一组解,求解这组解。

这里用到了差分约束系统,不懂得可以去看一下。

这里简单介绍求解的方法:

首先整理一下不等式: SiSjk —-> SjSik —-> Sjsik

还有隐藏的不等式: 0SiSi11

于是,我们把 i j连一条边权为 k 的边,然后从 n 做一次最短路,就可以得出所有的Si,这样 SiSi1 就是每个点是否有收费站。

这样,第一个问题就解决了。

第二个问题让我们求使用k次引擎 1 ~n的最短路。

那么有了第一问的条件,我们可以求出两点之间的最短路。

对于求解有很多方法,这里不一一介绍:

我们设 dpi,j 表示到 i 点用了j次引擎的最短路。那么转移可以在最短路算法中完成。

最后答案为 min(dpn,i)(0in)

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 301
using namespace std;
int n,m,E,P,L,K;
int d[N];
int b[N][N];
bool vis[N],s[N];
int f[N][N];
int dp[N][31];
int dl[N*N];
int bs[N*N];
bool bz[N][31];
void find()
{
    int l=0,r=1;
    memset(d,60,sizeof(d));
    dl[1]=n;
    d[n]=m;
    d[0]=0;
    vis[n]=true;
    while(l<r)
    {
        l++;
        int x=dl[l];
        fo(i,1,n)
        if(d[x]+b[x][i]<d[i])
        {
            d[i]=d[x]+b[x][i];
            if(!vis[i])
            {
                vis[i]=true;
                dl[++r]=i;
            }
        }
        vis[x]=false;
    }
}
void spfa()
{
    int l=0,r=1;
    memset(dp,60,sizeof(dp));
    memset(dl,0,sizeof(dl));
    memset(bs,60,sizeof(bs));
    dp[1][0]=0;
    dl[1]=1;
    bs[1]=0;
    vis[1]=true;
    while(l<r)
    {
        l++;
        int x=dl[l],t=bs[l];
        fo(i,1,n)
        if(i!=x)
        {
            if(dp[x][t]+f[x][i]<dp[i][t])
            {
                dp[i][t]=dp[x][t]+f[x][i];
                if(!bz[i][t])
                {
                    bz[i][t]=true;
                    r++;
                    dl[r]=i;
                    bs[r]=t;
                }
            }
            if(t<K && f[x][i]<=L && dp[i][t+1]>dp[i][t])
            {
                dp[i][t+1]=dp[x][t];
                if(!bz[i][t+1])
                {
                    bz[i][t+1]=true;
                    r++;
                    dl[r]=i;
                    bs[r]=t+1;
                }
            }
        }
        bz[x][t]=false;
    }
}
int main()
{
    freopen("3430.in","r",stdin);
    freopen("3430.out","w",stdout);
    cin>>n>>m>>E>>P>>L>>K;
    memset(f,60,sizeof(f));
    fo(i,1,E)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        f[u][v]=f[v][u]=min(f[u][v],w);
    }
    fo(i,1,n) f[i][i]=0;
    memset(b,60,sizeof(b));
    fo(i,1,P)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        b[v][u-1]=min(b[v][u-1],-w);
    }
    fo(i,1,n) b[i][i-1]=0,b[i-1][i]=1;
    find();
    fo(i,1,n) s[i]=d[i]-d[i-1];
    int tttt=0;
    fo(k,1,n)
    if(!s[k])
    {
        fo(i,1,n)
        fo(j,1,n)
        if(i!=j && i!=k && k!=j)
        f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
    }
    spfa();
    int ans=2147483647;
    fo(i,0,K) ans=min(ans,dp[n][i]);
    cout<<ans;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值