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\n",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\n",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;
}

猜你喜欢

转载自www.cnblogs.com/lajioj/p/9480808.html