• 【noip2017】逛公园


    题目描述

    策策同学特别喜欢逛公园。公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。

    策策每天都会去逛公园,他总是从1号点进去,从 N 号点出来。

    策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到 N 号点的最短路长为 d ,那么策策只会喜欢长度不超过 d + K 的路线。

    策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

    为避免输出过大,答案对 P 取模。

    如果有无穷多条合法的路线,请输出 -1 。


    输入

    第一行包含一个整数 T , 代表数据组数。

    接下来 T 组数据,对于每组数据: 第一行包含四个整数 N,M,K,P ,每两个整数之间用一个空格隔开。

    接下来 M 行,每行三个整数 ai,bi,ci ,代表编号为 ai,bi 的点之间有一条权值为 ci 的有向边,每两个整数之间用一个空格隔开(可能有0边)。

    对于 100%的数据, 1≤ P ≤ 109,N ≤ 100000 ,M ≤ 200000 ,1 ≤ ai,bi ≤ N,0 ≤ ci ≤ 1000。


    输出

    输出文件包含 T 行,每行一个整数代表答案。


    样例输入

    2
    5 7 2 10
    1 2 1
    2 4 0
    4 5 2
    2 3 2
    3 4 1
    3 5 2
    1 5 3
    2 2 0 10
    1 2 0
    2 1 0


    样例输出

    3
    -1
    



    题解

    这就是DAY1 的dp了。

    首先很容易想到先spfa求一遍最短路(实际上并不需要求出最短路)。接下来我们要反向建图,再做一遍spfa求出终点 n 到各点的最短路 d[ i ] ,于是在每个点我们都可以知道它离终点的最短距离,然后当我们从点 i 往下一个点 j 走的时候,如果d[ j ] + dis[ i ][ j ] > d[ i ] ,说明我们走了冤枉路,多走的冤枉路的值是 d[ j ] + dis[ i ][ j ] - d[ i ] 。因为我们不能选择大于最短路+k 的长度的路径,所以我们走的冤枉路的最大值就是 k,于是很容易的就想到了dp。

    用 dp[ i ][ j ] 表示在 i 号点还可以允许走 j 的冤枉路的方案数,考虑向下一个点 k 转移,转移方程为: dp[ i ][ j ] = Σ dp[ k ][   j - ( dis[ i ][ k ] + d[ k ] - d[ i ] )   ]   。

    什么情况输出“-1”呢?题目中说有 0 边,那么当出现 0 环的时候就有无穷多的情况了,只要判掉这种情况即可。

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int maxn=100000+50;
    const int maxm=200000+50;
    const int inf=2139062143;
    
    int n,m,pp,k,a,b,c,T;
    int fir[maxn],to[maxm],nex[maxm],wi[maxm],ecnt;
    int fi[maxn],t[maxm],ne[maxm],w[maxm],e;
    int d[maxn],q[30000001],dp[maxn][60];
    bool p[maxn],v[maxn][60],u[maxn];
    
    template<typename T>void read(T& aa){
        char cc; ll ff;aa=0;cc=getchar();ff=1;
        while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
    
    void add_edge(int u,int v,int ww){
        nex[++ecnt]=fir[u];fir[u]=ecnt;to[ecnt]=v;wi[ecnt]=ww;
        ne[++e]=fi[v];fi[v]=e;t[e]=u;w[e]=ww;
    }
    
    void init(){
        mem(fir,0);mem(to,0);mem(nex,0);mem(wi,0);ecnt=0;
        mem(fi,0);mem(t,0);mem(ne,0);mem(w,0);e=0;
        mem(u,0);mem(dp,-1);mem(v,0);
    }
    
    void spfa(int x){
        int head=0,tail=1;
        memset(p,false,sizeof(p));
        memset(d,127,sizeof(d));
        p[x]=true;
        d[x]=0;
        q[1]=x;
        while(head<tail){
            head++;
            int u=q[head];
            p[u]=false;
            for(int e=fi[u];e;e=ne[e]){
                int v=t[e];
                if(d[u]+w[e]<d[v]){
                    d[v]=d[u]+w[e];
                    if(!p[v]){
                        tail++;
                        p[v]=true;
                        q[tail]=v;
                    }
                }
            }
        }
    }
    
    int dfs(int a,int b){
        if(b<0) return 0;
        else if(v[a][b]) return -inf;
        else if(dp[a][b]!=-1) return dp[a][b];
        else{
            v[a][b]=1;int key=0;
            if(a==n) key++;
            for(int e=fir[a];e;e=nex[e]){
                int vv=to[e];
                if(!u[vv]) continue;
                int ww=dfs(vv,b-(d[vv]+wi[e]-d[a]));
                if(ww==-inf) return -inf;
                key=(key+ww)%pp;
            }
            dp[a][b]=key;
            v[a][b]=0;
            return key;
        }
    }
    
    int main(){
        read(T);
        while(T--){
            init();
            read(n),read(m),read(k),read(pp);
            for(int i=1;i<=m;i++){
                read(a),read(b),read(c);
                add_edge(a,b,c);
            }
            spfa(n);
            for(int i=1;i<=n;i++) if(d[i]!=inf) u[i]=true;
            int z=dfs(1,k);
            if(z==-inf) cout<<"-1"<<endl;
            else cout<<z<<endl;
        }
        return 0;
    }
  • 相关阅读:
    TypeScript 第一讲 ———— 基本数据类型的使用
    关于TypeScript命名空间
    Egret 自定义皮肤 ———— 引入类中以及createChildren()和 childrenCreated()的使用
    egret基础——控件
    回顾过去,展望未来
    JDBC、Hibernate、Mybatis之间的区别
    SSM框架优缺点和spring boot 比起优缺点是什么?
    拦截器和过滤器的区别
    转发和重定向区别
    关于虚拟机中克隆的linux为什么不能开启网络服务
  • 原文地址:https://www.cnblogs.com/rlddd/p/9502351.html
Copyright © 2020-2023  润新知