zoukankan      html  css  js  c++  java
  • NOIP2017提高组——逛公园

    【题面】:
    逛公园

    思路:

    此题还是很有挑战性,考场上绝大部分人都只打了暴力((#include<me>))。
    首先我觉得(k=0)(30)分的暴力还是比较简单,和最短路计数一样,直接暴力就(ok)

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define inf 0x3f3f3f3f
    using namespace std;
    
    const int MAXM = 200005;
    const int MAXN = 100005;
    int n,m,k,p;int ans=0;int minn;
    
    struct edge{
        int u,v,w,nxt;
    }e[MAXM<<1];int head[MAXN];int cnt=0;int dis[MAXN];bool r[MAXN];bool vis[MAXN];
    
    inline void add(int u,int v,int w){
        e[++cnt].u = u;e[cnt].v = v;e[cnt].w = w;e[cnt].nxt = head[u];head[u] = cnt;
    }
    
    inline void spfa(){
        queue<int>q;
        q.push(1);
        memset(dis,inf,sizeof dis);
        dis[1] = 0; 
        while(!q.empty()){
            int u = q.front();q.pop();r[u] = 0;
            for(int i=head[u];i;i=e[i].nxt){
                int v = e[i].v;
                if(dis[v] > dis[u] + e[i].w){
                    dis[v] = dis[u] + e[i].w;
                    if(!r[v]){
                        r[v] = 1;
                        q.push(v); 
                    }
                }
            }
        }
    }
    
    inline void dfs(int x,int len){
        if(len > minn + k) return ;
        if(x == n){
            ans++;
            ans %= p;
            return ;
        }
        vis[x] = 1;
        for(int i=head[x];i;i=e[i].nxt){
            int v = e[i].v;
            if(!vis[v]){
                dfs(v,len+e[i].w);
                vis[v] = 0;
            }
        }
    }
    
    inline void clear(){
        memset(head,0,sizeof head);
        cnt=0;
        memset(dis,0,sizeof dis);
        memset(r,0,sizeof r);
        memset(vis,0,sizeof vis);
        ans=0;
    }
    
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            clear();
            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();
            minn = dis[n];
            dfs(1,0);
            printf("%d
    ",ans%p);
        }
        return 0;
    }
    

    然后考虑满分做法,直接暴力时我们发现,有很多节点是被重复访问了,于是可以考虑记忆化搜索。用(f[i][j])表示到(i)这个节点,多走了(j)的路程的方案数,(obviously) (0<=j<=k),应为(k_{max}=50),所以该状态的定义可以表示。然后从(1)(k)扫一遍,记录下所有(f[i][j])的值。
    那么如何判断(0)环呢?就记录一个(vis[i][j])的数组,定义和(f)一样,如果当前访问的节点已经(vis)过了,就说明有(0)环,记得最后还要跑一遍(dfs(n,k+1)),因为有些(0)环的情况可能未被更新到。

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #define inf 0x3f3f3f3f
    using namespace std;
    
    int T;int n,m,k,p;
    
    const int MAXN = 100005;
    const int MAXM = 200005;
    
    struct edge{
        int u,v,w,nxt;
    }e[MAXM],E[MAXM];int Hd[MAXN];int head[MAXN];int cnt=0;
    
    inline void add(int u,int v,int w){
        e[++cnt].u = u;e[cnt].v = v;e[cnt].w = w;e[cnt].nxt = head[u];head[u] = cnt;
        E[cnt].u = v;E[cnt].v = u;E[cnt].w = w;E[cnt].nxt = Hd[v];Hd[v] = cnt;
    }
    
    queue<int>q;int dis[MAXN];bool r[MAXN];
    int f[MAXN][51],vis[MAXN][51];bool err = 0;
    
    inline void spfa(){
        q.push(1);memset(dis,inf,sizeof dis);
        dis[1] = 0;
        while(!q.empty()){
            int u = q.front();q.pop();r[u] = 0;
            for(int i=head[u];i;i=e[i].nxt){
                int v = e[i].v;
                if(dis[v] > dis[u] + e[i].w){
                    dis[v] = dis[u] + e[i].w;
                    if(!r[v]){
                        r[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
    }
    
    inline int dfs(int u,int waste){
        if(~f[u][waste]) return f[u][waste];
        vis[u][waste] = 1;
        f[u][waste] = 0;
        for(int i=Hd[u];i;i=E[i].nxt){
            int v = E[i].v;
            int left = dis[u]-dis[v]+waste-E[i].w;
            if(left < 0) continue;
            if(vis[v][left]) err = 1;
            f[u][waste] += dfs(v , left);
            f[u][waste] %= p;
        }
        vis[u][waste] = 0;
        return f[u][waste];
    }
    
    inline void calc(){
        f[1][0] = 1;int ans = 0;
        for(int i=0;i<=k;++i){
            ans += dfs(n,i),ans%=p;
        }
        dfs(n,k+1);
        if(err){puts("-1");return ;}
        printf("%d
    ",ans);
    }
    
    inline void clear(){
        memset(head,0,sizeof head);
        memset(Hd,0,sizeof Hd);
        memset(vis,0,sizeof vis);
        memset(f,-1,sizeof f);
        memset(r,0,sizeof r);
        err = 0;cnt = 0;
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            clear();
            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();
            
            calc();
        }
        return 0;
    }
    
  • 相关阅读:
    SQL Server 错误15401:没有找到Windows NT用户或组‘EMLE\ASPNET’
    使用 TestContext 类
    C# 动态2维数组
    2010年春季 软件测试技术 软件项目管理 实验安排
    代码覆盖率结果:没有为此测试运行启用代码覆盖率
    InternetGetCookie/InternetSetCookie (WinInet) changed with Internet Explorer 7
    C# 替换文本文件中的某一行 (要求此文件存在)
    android 复制 粘贴 功能实现
    java动态代理(JDK和cglib)
    使用ant打web应用更新包
  • 原文地址:https://www.cnblogs.com/lajioj/p/9480808.html
Copyright © 2011-2022 走看看