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
  • 相关阅读:
    Matlab 绘制三维立体图(以地质异常体为例)
    Azure DevOps的variable group实现array和hashtable参数的传递
    Azure DevOps 利用rest api设置variable group
    Azure AADSTS7000215 其中一种问题的解决
    Power BI 实现实时更新Streaming Dataset
    AAD Service Principal获取azure user list (Microsoft Graph API)
    Matlab 沿三维任意方向切割CT图的仿真计算
    Azure Powershell script检测登陆并部署ARM Template
    Azure KeyVault设置策略和自动化添加secrets键值对
    Azure登陆的两种常见方式(user 和 service principal登陆)
  • 原文地址:https://www.cnblogs.com/yyys-/p/11353166.html
Copyright © 2011-2022 走看看