zoukankan      html  css  js  c++  java
  • 逛公园

    传送门

    解法:

    先不考虑0环
    很容易想到dp
    状态转移方程也很容易想到

    (d[i])为n到i的最短路长度 当然此时是反向图
    反向图是为了防止1能到达的点到达不了n而出错
    (dp[i][j])表示到达i点距离为(d[i]+j)的路径个数

    x->y有路径 (dp[x][k]=dp[y][k-(d[y]+edge(x,y)-d[x])] (0le d[y]+edge(x,y)-d[x]le k)) 此时为正向图

    当然dp在这种图中不好实现
    改成记忆化搜索


    有些点反向图n到达的了而正向图1到达不了
    这些点在正向图搜索时不可能收到
    而当
    有些点反向图n到达不了而正向图1到达的了
    这些点的d[]值为“正无穷”
    此时(d[y]+edge(x,y)-d[x])要么小于0要么大于k
    所以对答案也无影响

    再考虑0环
    若一次深搜时出现两次(i,j)
    那么一定有0环 想一想就知道了

    这时输出-1结束就行

    否则 最终答案就是 (sum_{i=0}^kdp[1][i])

    代码:

    第一种:最好理解 前缀和在主程序中处理 但是评测姬一累就会凉凉

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<map>
    #define inf 2000000000
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define rep(i,a,b) for(register int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(register int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int T,n,m,k,mod,ans;
    int d[100010],dp[100010][55];
    int tot,head[100010],tt,rev[100010];
    bool cmp,vis[100010],v[100010][55];
    queue<int> que;
    struct EDGE
    {
        int to,nxt,d;
    }edge[200010],reve[200010];
    inline void add(int u,int v,int d)
    {
        edge[++tot].to=v;
        edge[tot].d=d;
        edge[tot].nxt=head[u];
        head[u]=tot;
    }
    inline void addr(int u,int v,int d)
    {
        reve[++tt].to=v;
        reve[tt].d=d;
        reve[tt].nxt=rev[u];
        rev[u]=tt;
    }
    inline int read()
    {
        int x=0;
        char ch=getchar();
        while(ch<'0'||ch>'9')
            ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            x=(x<<3)+(x<<1)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    inline void spfa()
    {
        d[n]=0;
        que.push(n);
        while(!que.empty())
        {
            int x=que.front();que.pop();
            vis[x]=0;
            for(int i=rev[x];i;i=reve[i].nxt)
            {
                int y=reve[i].to,dis=reve[i].d;
                if(d[y]>d[x]+dis)
                {
                    d[y]=d[x]+dis;
                    if(!vis[y]) vis[y]=1,que.push(y);
                }
            }
        }
    }
    int getdp(int x,int t)
    {
        if(v[x][t]){v[x][t]=0;cmp=1;return 0;}
        if(dp[x][t]) return dp[x][t];
        v[x][t]=1;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            int dis=t-(d[y]+edge[i].d-d[x]);
            if(dis<0||dis>k) continue;
            dp[x][t]=(dp[x][t]+getdp(y,dis))%mod;
            if(cmp){v[x][t]=0;return 0;}
        }
        v[x][t]=0;
        if(x==n&&t==0) dp[x][t]=1;//为了排除过终点的零环 终点初始值需要放在此处处理
        return dp[x][t];
    }
    int main()
    {
        T=read();
        while(T--)
        {
            n=read(),m=read(),k=read(),mod=read();
            tot=1,tt=1;
            memset(head,0,sizeof(head));
            memset(rev,0,sizeof(rev));
            memset(d,0x3f,sizeof(d));
            memset(dp,0,sizeof(dp));
            rep(i,1,m)
            {
                int u=read(),v=read(),d=read();
                add(u,v,d);addr(v,u,d);
            }
            spfa();
            ans=0,cmp=0;
            dwn(i,k,0)
                ans=(ans+getdp(1,i))%mod;
            if(cmp) printf("-1
    ");
            else printf("%d
    ",ans);
        }
        return 0;
    }
    
    

    第二种:getdp()在一边跑一边处理前缀和 很快

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<map>
    #define inf 2000000000
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define rep(i,a,b) for(register int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(register int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int T,n,m,k,mod,ans;
    int d[100010],dp[100010][55];
    int tot,head[100010],tt,rev[100010];
    bool cmp,vis[100010],v[100010][55];
    queue<int> que;
    struct EDGE
    {
        int to,nxt,d;
    }edge[200010],reve[200010];
    inline void add(int u,int v,int d)
    {
        edge[++tot].to=v;
        edge[tot].d=d;
        edge[tot].nxt=head[u];
        head[u]=tot;
    }
    inline void addr(int u,int v,int d)
    {
        reve[++tt].to=v;
        reve[tt].d=d;
        reve[tt].nxt=rev[u];
        rev[u]=tt;
    }
    inline int read()
    {
        int x=0;
        char ch=getchar();
        while(ch<'0'||ch>'9')
            ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            x=(x<<3)+(x<<1)+(ch^48);
            ch=getchar();
        }
        return x;
    }
    inline void spfa()
    {
        d[n]=0;
        que.push(n);
        while(!que.empty())
        {
            int x=que.front();que.pop();
            vis[x]=0;
            for(int i=rev[x];i;i=reve[i].nxt)
            {
                int y=reve[i].to,dis=reve[i].d;
                if(d[y]>d[x]+dis)
                {
                    d[y]=d[x]+dis;
                    if(!vis[y]) vis[y]=1,que.push(y);
                }
            }
        }
    }
    int getdp(int x,int t)
    {
        if(v[x][t]){v[x][t]=0;cmp=1;return 0;}
        if(dp[x][t]) return dp[x][t];
        v[x][t]=1;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            int dis=t-(d[y]+edge[i].d-d[x]);
            if(dis<0||dis>k) continue;
            dp[x][t]=(dp[x][t]+getdp(y,dis))%mod;
            if(cmp){v[x][t]=0;return 0;}
        }
        v[x][t]=0;
        if(x==n) dp[x][t]+=1;//对比第一种 其实就是此处改了 因为dp[1][k]->dp[n][i]与dp[1][k-i]->dp[n][0]所得值为相同的 所以给dp[n][]全部赋值为1 用一次getdp(1,k)即可过
        return dp[x][t];
    }
    int main()
    {
        T=read();
        while(T--)
        {
            n=read(),m=read(),k=read(),mod=read();
            tot=1,tt=1;
            memset(head,0,sizeof(head));
            memset(rev,0,sizeof(rev));
            memset(d,0x3f,sizeof(d));
            memset(dp,0,sizeof(dp));
            rep(i,1,m)
            {
                int u=read(),v=read(),d=read();
                add(u,v,d);addr(v,u,d);
            }
            spfa();
            cmp=0;
            ans=getdp(1,k);
            if(cmp) printf("-1
    ");
            else printf("%d
    ",ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    深入理解jvm分享培训pdf(转载) 老李
    innobackupex自动备份脚本(增量备份,自动压缩)
    多线程调用生成主键流水号存储过程产生主键冲突问题解决方案
    mysql 5.7新数据库sys解析(一)
    根据日期累加金额的mysql
    mysql字符串分割函数(行转列)
    使用innobackupex备份mysql数据库
    css学习inlineblock详解及dispaly:inline inlineblock block 三者区别精要概括
    html良好结构之豆瓣风格
    HTML5学习笔记html5与传统html区别
  • 原文地址:https://www.cnblogs.com/MYsBlogs/p/11068548.html
Copyright © 2011-2022 走看看