逛公园题解

这道题是noip2017原题。

首先,首先你可以思考一下,第一天dp不见了。似乎有很多人这么说。那么前两道题 我猜的 显然不是DP,所以说这道题可以用DP解。

但是我用的方法是记忆化搜索。因为DP这个东西 我太蒻了,不会  没有记忆化优美。

具体做法是跑一次反向的最短路,令f[u][k] 表示 dis(u,n)<=MinDis(u,n)+k的方案数,答案就是 f[1][K]。(从1号点到n好点不超过k的方案数)

考虑边(u,v,w)。

如果走这条边的话, dis(v,n)=MinDis(v,n)+w-MinDis(u,n) f[u][k]=f[v][k(MinDis(v,n)MinDis(u,n)+w)]

这样怎么判 0 环呢?只要加个标记判断一下就可以了。

还有一点要注意,就是必须先写一个函数判0环,再求值。不然的话可能会玄学死循环。

还有,就是f初值必须是1,赋值成0可能会原地爆炸。

 

代码实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<climits>
#define int long long
#define maxa 400005
#define mem(x) memset(x,0,sizeof(x))
#define mp(a,b) make_pair(a,b)
using namespace std;
void read(int &x){
    x=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        c=getchar();
    }
    while(c<='9'&&c>='0'){
        x=((x<<1)+(x<<3))+c-48;
        c=getchar();
    }
    return ;
}

int n,m,k,mod;
int to[maxa],nxt[maxa],h[maxa],top,val[maxa];
int To[maxa],Nxt[maxa],H[maxa],Top,Val[maxa];
void psh(int u,int v,int va){
    to[++top]=v,nxt[top]=h[u],h[u]=top,val[top]=va;
    To[++Top]=u,Nxt[Top]=H[v],H[v]=Top,Val[Top]=va;
    return ;
}

int dis[maxa],vis[maxa];
void AC_DREAM(){//迪杰斯特拉
    priority_queue <pair<int,int>,vector<pair<int,int> >,greater <pair<int,int> > >q ;
    q.push(mp(0,1));
    while(!q.empty()){
        int now=q.top().second;q.pop();
        if(vis[now])continue;
        vis[now]=1;
        for(register int i=h[now];i;i=nxt[i]){
            int y=to[i];
            if(dis[y]>dis[now]+val[i])dis[y]=dis[now]+val[i],q.push(mp(dis[y],y));
        }
    }
    return ;
}
int dp[maxa][51];
int bl[maxa][51];
bool yy;
int num,y;
int dfs(int x,int kk){//一定要写两遍,反正差不多,复制一下改一改就可以了。。。
    if(~dp[x][kk])return dp[x][kk];
    bl[x][kk]=1,dp[x][kk]=0;
    for(register int i=H[x];i;i=Nxt[i]){
        y=To[i];
        num=dis[x]-dis[y]+kk-Val[i];
        if(num<0)continue;
        dp[x][kk]=(dp[x][kk]+dfs(y,num))%mod;
    }
    bl[x][kk]=0;
    return dp[x][kk];
}
void dfs1(int x,int kk){//非常重要,必须分开写,不然玄学TLE
    if(yy)return ;
    if(~dp[x][kk])return ;
    bl[x][kk]=1,dp[x][kk]=0;
    for(register int i=H[x];i;i=Nxt[i]){
        y=To[i];
        num=dis[x]-dis[y]+kk-Val[i];
        if(num<0)continue;
        if(bl[y][num]){
            yy=1;
            return ;
        }
        dfs1(y,num);
        
    }
    bl[x][kk]=0;
    return ;
}
void clear(){
    yy=Top=top=0;
    mem(to),mem(nxt),mem(h),mem(val);
    mem(To),mem(Nxt),mem(H),mem(Val);
    
    mem(vis);
    for(register int i=2;i<=n;i++)dis[i]=1000000000;
    
    mem(bl);
    
}


#undef int
int main(){
    #define int long long
    int T;
    read(T);
    while(T--){
        read(n),read(m),read(k),read(mod);
        clear();
        for(register int i=1,u,v,va;i<=m;i++){
            read(u),read(v),read(va);
            psh(u,v,va);
        }
        AC_DREAM();
//        for(int i=1;i<=top;i++)cout<<dis[i]<<" ";
//        cout<<endl;
        mem(vis);
        int ans=0;
        memset(dp,-1,sizeof(dp));//一定要赋值成-1!
        for(register int i=0;i<=k;i++)dfs1(n,i);
        if(yy){
            cout<<-1<<endl;
            continue;
        }
        memset(dp,-1,sizeof(dp));//一定要赋值成-1!
        dp[1][0]=1;
        for(register int i=0;i<=k;i++)ans=(ans+dfs(n,i))%mod;
        cout<<ans<<endl;
    }
    
}

 

posted @ 2019-08-27 17:28  WangQT  阅读(284)  评论(0编辑  收藏  举报