zoukankan      html  css  js  c++  java
  • 【NOIP2017】逛公园

    题面

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

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

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

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

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

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

    分析

    本人思路(WA思路)
    首先要求最短路,还要判0环。(然而判0环我好像不会诶??)
    难道要写个tarjan?然后看1到哪些点的距离相等,检验这些点在不在一个环内?
    然后再考虑要维护的信息
    走到哪个点和除去最短路用了多少距离了
    所以f[i][j]表示从第i个点走到第n个点,还可以用距离j的路径数(不一定用完)
    怎么转移呢?
    f[v][j-e[u][v]]+=f[u][j]
    好像只有这样写转移方程才比较方便诶
    那就考虑记忆化搜索吧
    边界条件 f[n][0]=1.

    看完题解后的新思路(AC思路)
    再看自己的思路仿佛是在搞笑一样,几乎只有状态定义对了
    首先,记忆化搜索过程中,判断零环的方法是在DFS时,记录递归栈,若重复搜索了一点,则一定存在零环,直接退出。
    而且动态转移方程也没有这么naive啊。。
    因为从u到n本来的最短是d[u],如果选择了(u,v,w)这条边,那u到n的距离就变为w+d[v]
    而我们维护了还可以用的距离j 所以u在选择这条边后的j应该变为 j-((w+d[v])-d[u]) (j>=((w+d[v])-d[u]))
    所以 f[u][j]+=f[v][j-((w+d[v])-d[u])]
    初始边界条件是f[n][j]=1 (0<=j<=k),答案就是f[1][k]

    需要注意的是,d[u]求的是u到n的最短距离,所以跑spfa需要反向建图。而记忆化搜索的时候只能用原图,两个图要分开

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100010
    int t,n,m,k,p,cnt,cot;
    int d[N],first[N],head[N],vis[N],inque[N][55],f[N][55];
    struct email
    {
        int u,v,w;
        int nxt;
    }e[N*5],g[N*5];
    queue<int>q;
    inline void add(int u,int v,int w)
    {
        e[++cnt].nxt=first[u];first[u]=cnt;
        e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;
    }
    inline void spfa_add(int u,int v,int w)
    {
        g[++cot].nxt=head[u];head[u]=cot;
        g[cot].u=u;g[cot].v=v;g[cot].w=w;
    }
    inline void init()
    {
        cnt=cot=0;
        memset(f,0,sizeof(f));
        memset(d,0x3f,sizeof(d));
        memset(head,0,sizeof(head));
        memset(first,0,sizeof(first));
        memset(inque,0,sizeof(inque));
    }
    
    void spfa(int x)
    {
        q.push(x);vis[x]=1;d[x]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();vis[u]=0;
            for(int i=head[u];i;i=g[i].nxt)
            {
                int v=g[i].v,w=g[i].w;
                if(d[v]>d[u]+w)
                {
                    d[v]=d[u]+w;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    inline int dfs(int u,int j)
    {
        
        if(f[u][j])return f[u][j];
        if(inque[u][j]) 
        return f[u][j]=-1;    
        inque[u][j]=-1;
        if(u==n) f[u][j]=1;
        for(int i=first[u];i;i=e[i].nxt)
        {
            int v=e[i].v,w=e[i].w;
            int rem=d[v]+w-d[u];
            if(rem>j)continue;    
            int ret=dfs(v,j-rem);
            if(ret==-1) return f[u][j]=-1;
            f[u][j]=(f[u][j]+ret)%p;
        }
        inque[u][j]=0;
        return f[u][j];
    }
    
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            init();
            scanf("%d%d%d%d",&n,&m,&k,&p);
            for(int i=1;i<=m;i++)
            {
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);spfa_add(v,u,w);
            }
            spfa(n);
            printf("%d
    ",dfs(1,k));
        }
    }
    “Make my parents proud,and impress the girl I like.”
  • 相关阅读:
    [转] 常用的sql server规范
    <深入理解JavaScript>学习笔记(2)_揭秘命名函数表达式
    SQL Server 字段类型 decimal(18,6)小数点前是几位?记一次数据库SP的BUG处理
    <深入理解JavaScript>学习笔记(1)_编写高质量JavaScript代码的基本要点
    [转] SQL函数说明大全
    jquery 获取 tagName(JQuery如何得到tagName?)
    [转] .NET出现频率非常高的笔试题
    [转] SQL Server游标的使用
    jquery datatables 学习笔记
    [转] 如何让代码可测试化(C#)
  • 原文地址:https://www.cnblogs.com/NSD-email0820/p/9737470.html
Copyright © 2011-2022 走看看