• 洛谷3953:逛公园——题解


    https://www.luogu.org/problemnew/show/P3953

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

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

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

    策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗? 为避免输出过大,答案对p取模。 如果有无穷多条合法的路线,请输出−1。

    相信自己多做点dp题就能把思维提上来的我是不是很sb……

    参考洛谷第一篇题解。

    没记错当时奔着70pts去的结果30pts。

    30pts很简单就不讲了。

    70pts设f[i][j]为1~i路径长度比最短距离<=j的个数。

    然后dij对dis处理之后对dis排序再进行更新。//我就是错在这个地方。

    100pts其实0边也很好处理,拓扑找即可。

    但是放在考场上会被卡常。

    所以我们选择记忆化搜索。

    但是如果普通的搜的话就又会犯我上面的错误。

    思考错误的产生原因:即我们的偏移量k可能会出问题因为有些点有可能到不了n。

    所以我们先dij反图处理点到n的距离,再在正图跑即可。

    至于0环的判断,只要同一状态被dfs时搜过两次就一定是进了0环。

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    const int N=100010;
    const int M=200010;
    const int K=51;
    const int INF=1e9;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct node{
        int w,to,nxt;
    }e[2*M];
    int n,m,k,p,cnt,head[N],dis[N],f[N][K];
    bool vis[N],in[N][K];
    struct cmp{
        bool operator ()(int a,int b){
            return dis[a]>dis[b];
        }
    };
    priority_queue<int,vector<int>,cmp>q;
    inline void add(int u,int v,int w){
        e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
    }
    void bfs(int s){
        dis[s]=0;vis[s]=1;q.push(s);
        while(!q.empty()){
            int u=q.top();q.pop();vis[u]=0;
            for(int i=head[u];i;i=e[i].nxt){
                if(i&1)continue;
                int v=e[i].to,w=e[i].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    if(!vis[v]){
                        vis[v]=1;q.push(v);
                    }
                }
            }
        }
    }
    int dp(int u,int t){
        if(in[u][t])return f[u][t]=-1;
        if(f[u][t])return f[u][t];
        if(u==n)f[u][t]=1;
        in[u][t]=1;
        for(int i=head[u];i;i=e[i].nxt){
            if(!(i&1))continue;
            int v=e[i].to,w=e[i].w;
            int tmp=dis[v]+w-dis[u];
            if(tmp<=t){
                int tot=dp(v,t-tmp);
                if(tot==-1)return f[u][t]=-1;
                f[u][t]=(f[u][t]+tot)%p;
            }
        }
        in[u][t]=0;
        return f[u][t];
    }
    inline void init(){
        cnt=0;
        for(int i=1;i<=n;i++){
            dis[i]=INF,vis[i]=head[i]=0;
            for(int j=0;j<=k;j++)f[i][j]=in[i][j]=0;
        }
    }
    int main(){
        int t=read();
        while(t--){
            n=read(),m=read(),k=read(),p=read();
            init();
            for(int i=1;i<=m;i++){
                int u=read(),v=read(),w=read();
                add(u,v,w);add(v,u,w);
            }
            bfs(n);
            printf("%d
    ",dp(1,k));
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    PHP反射
    Ramda函数式编程之PHP
    双因素算法存疑
    百度编辑器复制微信图片无法保存
    go语言可变参数的坑
    xorm的sql builder
    vue前后分离动态路由和权限管理方案
    分布式唯一id生成器的想法
    Java IO 之 InputStream源码
    图解 & 深入浅出Java初始化与清理:构造器必知必会
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8848094.html
Copyright © 2020-2023  润新知