zoukankan      html  css  js  c++  java
  • 逛公园(NOIP2017)

    传送门

    因为是求路径条数,而k的范围不大,想到用dp转移。

    用f[i][j]表示从n到i的距离<=最短距离+j的方案数。

    用d[i]表示从i点到n点的最短距离。

    对于边<u,v,val>

    f[u][j]=f[v][j+d[u]-d[v]-val]。

    记忆化搜索实现dp过程。

    小技巧:

    1.搜索时从n开始往前递归,使得原本就走不到n的点不会加入,节约时间。

    2.访问vis在退出时就清0,就不用每次都memset。

    #include<bits/stdc++.h>
    #define M 200003
    #define N 100003
    #define INF 2100000001
    #define LL long long 
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    int p,n,k;
    int head1[N],head2[N];
    int d[N],vis[N][52],f[N][52];
    int tot1=0,tot2=0;
    struct EDGE1{
        int nextt,to,val; 
    }w1[M];
    struct EDGE2{
        int nextt,to,val;
    }w2[M];
    void add1(int a,int b,int v)
    {
        tot1++;
        w1[tot1].nextt=head1[a];
        w1[tot1].to=b;
        w1[tot1].val=v;
        head1[a]=tot1;
    }
    void add2(int a,int b,int v)
    {
        tot2++;
        w2[tot2].nextt=head2[a];
        w2[tot2].to=b;
        w2[tot2].val=v;
        head2[a]=tot2;
    }
    
    LL dp(int now,int left)
    //反向跑是因为可以减少跑死胡同的情况增加效率,
    //一些不能到达n的路径就可以不用跑了
    {
        LL ans=0;
        if(left<0||left>k) return 0;
        if(vis[now][left])
        {
            vis[now][left]=0;//这些地方就赋为0,就不用再每次memset
            return -1; 
        }
        if(f[now][left]!=-1)return f[now][left];
        vis[now][left]=1;
        for(int i=head2[now];i;i=w2[i].nextt)
        {
            int v=w2[i].to;
            LL val=dp(v,left+d[now]-d[v]-w2[i].val);
            if(val==-1)
            {
                vis[now][left]=0;//这些地方就赋为0,就不用再每次memset
                return -1;
            }
            else ans=(ans+val)%p;
        }
        vis[now][left]=0;
        if(now==1&&left==0)ans++;
        f[now][left]=ans;//记忆化 
        return ans;
    }
    void SPFA(int s,int t)
    {
        queue<int>q;
        for(int i=1;i<=n;++i)
          d[i]=INF;
        memset(f,-1,sizeof(f));//记得初始化
        if(!q.empty())q.pop();
        q.push(s);d[s]=0;
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head1[x];i;i=w1[i].nextt)
            {
                int v=w1[i].to;
                if(d[x]+w1[i].val<d[v])
                {
                    d[v]=d[x]+w1[i].val;
                    q.push(v);
                }
            }
        }
        LL ans=0;
        for(int i=0;i<=k;++i)
        {
            LL res=dp(t,i);//dp 枚举k:0-k
            if(res==-1){printf("-1
    ");return;}
            else ans=(ans+res)%p;
        }
        printf("%lld
    ",ans);
    }
    int main()
    {
        int T=read();
        while(T--)
        {
            n=read();int m=read();k=read();p=read();
            memset(head1,0,sizeof(head1));
            memset(head2,0,sizeof(head2));
            tot1=0;tot2=0;
            for(int i=1;i<=m;++i)
            {
                int a=read(),b=read(),c=read();
                add1(a,b,c);add2(b,a,c);
            }
            SPFA(1,n);
        } 
    }
    View Code
  • 相关阅读:
    跟layout_weight说88,轻松搞定百分比布局
    跟闪退、程序崩溃说88
    程序的需求层次
    开博
    第十章 数组与集合 发牌程序 实例代码
    C#面向对象基础01
    winform form
    html
    C#语言基础02
    C#语言基础01
  • 原文地址:https://www.cnblogs.com/yyys-/p/11353166.html
Copyright © 2011-2022 走看看