zoukankan      html  css  js  c++  java
  • NOIP2017逛公园(park)解题报告

    park作为今年noipday1最后一道题还是相比前面几道题还是有点难度的
    首先你可以思考一下,第一天dp不见了,再看一下这题,有向图,看起来就比较像一个dp,考虑dp方程,首先肯定有一维是到哪个节点,还有一维肯定与路径长度有关,显然第二位就记录超过最短路多少。
    这样我们可以找到dp方程,首先枚举一个kk(0<=kk<=k),按拓扑序枚举每一个点,枚举以这个点为起点的路径,如果这条路在最短路上,那么dp[v][kk]+=dp[u][kk],else 如果dis[u]+kk+路径长度<=dis[v]+k则也可以进行类似的转移
    首先我们先正着跑一遍spfa,再反着跑一遍,以为要判断一个点在不在最短路上,只需要看起点到它的最短路+终点到它的最短路之和是否等于起点到终点的最短路
    接下来如果这条边在最短路上则这条路终点入度+1,再拓扑排序一遍顺便检查出有无0环,接下来就可以dp了
    接下来看代码吧

    #include<bits/stdc++.h>
    using namespace std;
    typedef int sign;
    typedef long long ll;
    #define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
    #define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
    const int N=1e5+5;
    bool cmax(sign &a,sign b){return a<b?a=b,1:0;}
    bool cmin(sign &a,sign b){return a>b?a=b,1:0;}
    template<typename T>T read()
    {
        T ans=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar();
        return ans*f;
    }
    void file()
    {
        #ifndef ONLINE_JUDGE
            freopen("park.in","r",stdin);
            freopen("park.out","w",stdout);
        #endif
    }
    int dis[N][2],in[N];
    int n,m,k,p;
    struct graph
    {
        int head[N],tt,nex[N<<1],w[N<<1],to[N<<1];
        inline void init()
    	{
    		tt=0;
    		For(i,1,n)head[i]=0;	
    	}
        inline void add(int x,int y,int z)
        {
            ++tt;
            w[tt]=z;to[tt]=y;
            nex[tt]=head[x];head[x]=tt;
        }
    }G[2];//因为双向建图,所以写在结构体里
    #define tra(G,i,u) for(register int i=G.head[u];i;i=G.nex[i])
    bool vis[N];
    inline void input()
    {
        int x,y,z;
        n=read<int>();m=read<int>();k=read<int>();p=read<int>();
        G[0].init();G[1].init();
    	For(i,1,m)
        {
            x=read<int>();y=read<int>();z=read<int>();
            G[0].add(x,y,z);G[1].add(y,x,z);
        }
    }
    queue<int>q;
    inline void spfa(int st,int now)
    {
        q.push(st);
        dis[st][now]=0;
        int u,v;
        while(!q.empty())
        {
            u=q.front();q.pop();
            vis[u]=0;
            tra(G[now],i,u)
            {
                v=G[now].to[i];
                if(cmin(dis[v][now],dis[u][now]+G[now].w[i]))
                {
                    if(!vis[v])
                    {
                        vis[v]=1;
                        //if(!q.empty()&&dis[v]<=dis[q.front()])q.push_front(v);
                        q.push(v);
                    }
                }
            }
        }
    }//跑spfa,因为要跑两遍,所以0表示正着的,1表示反着的
    int l[N];
    bool check()
    {
        int v,u;
        For(i,1,n)if(!in[i])l[++l[0]]=i;//入度为零入队
        For(j,1,l[0])
        {
            u=l[j];
            tra(G[0],i,u)
            {
                v=G[0].to[i];
                if(dis[v][0]==dis[u][0]+G[0].w[i])
                {
    	        //在最短路上就入度-1
                    --in[v];
                    if(!in[v])l[++l[0]]=v;
                }
            }
        }
        For(i,1,n)if(in[i]&&dis[i][0]+dis[i][1]<=dis[n][0]+k)return 1;
        //如果在最短路上且有环,则说明有0环
        return 0;
    }
    int dp[N][55];
    inline void add(int &a,int b)
    {
        a+=b;
        if(a>p)a-=p;
    }
    inline void topsort()
    {
        int v,u;
        dp[1][0]=1;
        For(kk,0,k)
        {
            For(j,1,l[0])
            {
                u=l[j];
                tra(G[0],i,u)
                {
                    v=G[0].to[i];
                    if(dis[v][0]==dis[u][0]+G[0].w[i])add(dp[v][kk],dp[u][kk]);//如果在最短路上,则沿着最短路走下去,当前比最短路大kk,则走到终点也一定比最短路大kk
                    else if(kk+dis[u][0]+G[0].w[i]<=dis[v][0]+k)
                    {
                        add(dp[v][kk+dis[u][0]+G[0].w[i]-dis[v][0]],dp[u][kk]);//走过这条路,当前比最短路大kk,则走过这条路后,又长了dis[u][0]+G[0].w[i]-dis[v][0]]
                    }
                }
            }
        }
        int ans=0;
        For(i,0,k)add(ans,dp[n][i]);
        printf("%d
    ",ans);
    }
    inline void work()
    {
        spfa(1,0);
        spfa(n,1);
        int v;l[0]=0;
        For(u,1,n)
        {
            tra(G[0],i,u)
            {
                v=G[0].to[i];
                if(dis[v][0]==dis[u][0]+G[0].w[i])in[v]++;//入度
            }
        }
        if(check())
        {
            puts("-1");
            return;
        }
        topsort();
    }
    const int inf=0x3f3f3f3f;
    inline void init()
    {
        For(i,1,n)For(j,0,1)dis[i][j]=inf;
    	For(i,1,n)in[i]=0;
        For(i,1,n)For(j,0,k)dp[i][j]=0;
        //初始化,写memset,在洛谷一直re
    }
    int main()
    {
        //从main函数看起是个好习惯
        file();
        int T=read<int>();
        while(T--)
        {
    	input();
    	init();
            work();
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    ajax_注册
    mysql 二
    mysql基础
    django数据库批量创建
    私有属性
    mysql操作
    @property @classmethod @staticmethod
    python中的__new__方法
    员工信息表-装逼版
    三级菜单
  • 原文地址:https://www.cnblogs.com/dengyixuan/p/7931721.html
Copyright © 2011-2022 走看看