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

    传送门

    这个题听说是NOIP2017最难的题?反正我当时啥也不会爆零……

    首先我们看送的分,直接跑最短路计数有30pts。

    之后就不会了hhhhh……

    我们仔细观察题目之后发现,这个题k的数据范围特别小,只有50,也就是说我们最多只会统计比最短路径长50的所有路径条数,那我们其实完全是可以这样DP的。

    具体操作如下,我们首先建出正图,在正图上跑一遍dij,记录起点到每个点的最短距离。之后再建出反图,处理出有哪些点是能到达终点的,这样到不了终点的点就可以被忽略。

    这样预处理之后,我们直接上大招:记忆化搜索!!!(这东西真的不是一般强力)

    我们传两个参,一个是当前的节点,另一个是当前剩余的距离(因为我们最多允许走比最短路长k的距离,所以我们可以在记忆化搜索的时候把它作为参数传进去)。这样的话记忆化搜索就变得很简单。首先如果这个点无法到达终点,直接返回即可,如果这个点已经存过值(dp数组已经被更新过),直接返回这个值就可以(记忆化)。否则的话我们继续进行深搜,减去当前所多花费的路程继续向下DFS即可。

    最后一个问题就是怎么判断0环。(因为如果出现无穷多条最短路的话一定是有0环出现)我们在记忆化搜索的时候使用vis数组记录当前是否被走过,在这个点结束dfs的时候将其还原。这样的话如果有0环,它会在vis被还原之前就走到这个点,那么就出现0环了。

    这样的话我们就做完了……不过这题卡常很厉害……我也不会用线段树求最短路,最后还是开O2勉强卡过去了。

    看一下代码。

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<vector>
    #include<set>
    #define pr pair<int,int>
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define sc second
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 100005;
    const int N = 10000005;
    const int INF = 2147483645;
     
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    int n,m,t,k,p,a,b,c,dis[M],dp[M][55],head[M],head1[M],ecnt,ecnt1;
    bool alive[M],vis[M][55];
    
    struct edge
    {
        int next,to,v;
    }e[M<<1],e1[M<<1];
    
    set <pr> q;
    set <pr> :: iterator it;
    
    void clear()
    {
        memset(e,0,sizeof(e));
        memset(e1,0,sizeof(e1));
        memset(head,0,sizeof(head));
        memset(head1,0,sizeof(head1));
        ecnt = ecnt1 = 0;
        rep(i,1,n)
        {
              alive[i] = 0,dis[i] = INF;
        rep(l,0,k) dp[i][l] = -1,vis[i][l] = 0;
        }
    }
    
    void add(int x,int y,int z)
    {
        e[++ecnt].to = y;
        e[ecnt].v = z;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    void add1(int x,int y,int z)
    {
        e1[++ecnt1].to = y;
        e1[ecnt1].v = z;
        e1[ecnt1].next = head1[x];
        head1[x] = ecnt1;
    }
    
    void dij()
    {
        int cnt = 0;
        dis[1] = 0;
        q.insert(mp(dis[1],1));
        while(!q.empty())
        {
        pr k = *(q.begin()); q.erase(q.begin());
        for(int i = head[k.sc];i;i = e[i].next)
        {
            if(dis[e[i].to] > dis[k.sc] + e[i].v)
            {
            it = q.find(mp(dis[e[i].to],e[i].to));
            if(it != q.end()) q.erase(it);
            dis[e[i].to] = dis[k.sc] + e[i].v;
            q.insert(mp(dis[e[i].to],e[i].to));
            }
        }
        }
    }
    
    void bfs()
    {
        queue <int> qu;
        qu.push(n);alive[n] = 1;
        while(!qu.empty())
        {
        int k = qu.front();qu.pop();
        for(int i = head1[k];i;i = e1[i].next)
        {
            int g = e1[i].to;
            if(!alive[g]) alive[g] = 1,qu.push(g);
        }
        }
    }
    
    int dfs(int a,int b)
    {
        if(b < 0) return 0;
        if(vis[a][b]) return -INF;
        if(dp[a][b] != -1) return dp[a][b];
        vis[a][b] = 1;
        int cur = 0;
        if(a == n) cur++;
        for(int i = head[a];i;i = e[i].next)
        {
        int di = dis[e[i].to] - dis[a];
        if(!alive[e[i].to]) continue;
        int w = dfs(e[i].to,b - (e[i].v - di));
        if(w == -INF) return -INF;
        else
        {
            cur += w;
            if(cur > p) cur -= p;
        }
        }
        dp[a][b] = cur % p;
        vis[a][b] = 0;
        return cur;
    }
    
    int main()
    {
        t = read();
        while(t--)
        {
        n = read(),m = read(),k = read(),p = read();
        clear();
        rep(i,1,m)
        {
            a = read(),b = read(),c = read();
            add(a,b,c),add1(b,a,c);
        }
        dij();//rep(i,1,n) printf("%d ",dis[i]);enter;
        bfs();//rep(i,1,n) printf("%d ",alive[i]);enter;
        int ans = dfs(1,k);
        if(ans == -INF) printf("-1
    ");
        else printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    MS SQL Server查询优化方法(收藏)
    .net打包并自动安装MS SQL Server数据库<转>
    解决oracle数据库监听器无法启动问题
    SQLServer2008/2005 /2000生成数据字典语句(转载)
    C# 常见错误处理(收藏)
    Oracle数据导入导出
    Oracle Database 9i/10g安装后的基本环境与服务
    Oracle to_char()函数的用法
    oracle参数列表
    SQL Server中存储过程比直接运行SQL语句慢的原因(转载)
  • 原文地址:https://www.cnblogs.com/captain1/p/9716419.html
Copyright © 2011-2022 走看看