luogu_P3953 逛公园 记忆化搜索+最短路

luogu_P3953 逛公园

记忆化搜索+最短路


题目链接
边权有0而且可能有环,所以可能出0环
出0环的话就有无限条路径了(因为可以绕着这个环一直转悠)
所以先正着跑spfa
然后反向建边
记忆化搜索
(f[i][j])表示到(i)点,还有比最短路增加(j)的余额的方案数。
真不好理解
再记录一个(vis[i][j])数组,(i),(j)的意义相同
但是这个存的是0/1,表示是不是之前访问过
假如之前访问过必然是0环(因为j没有减小)
向下转移的就是(j+dis[x]-dis[y]-len(x,y))
表示用(x)(y)之间的边代替最短路他们之间的距离能消耗多少(j)
又不好理解
看代码吧


代码如下:

#include<bits/stdc++.h>
using namespace std;
int T;
namespace wk{
const int maxk=60,maxn=100010;
int n,m,k,p,dis[maxn],inq[maxn],vis[maxn][maxk],f[maxn][maxk];
struct node{
    int to,dis;
    node(){}
    node(int _to,int _dis){to=_to;dis=_dis;}
};
vector<node> tu[maxn],ftu[maxn];
//DP
inline int dp(int now,int ls){
    int res=0;
    if(ls<0 || ls>k) return 0;
    if(vis[now][ls]){
        vis[now][ls]=0;return -1;
    }
    if(~f[now][ls]) return f[now][ls];
    vis[now][ls]=1;
    for(int i=0;i<ftu[now].size();i++){
        int y=ftu[now][i].to,z=ftu[now][i].dis;
        int val=dp(y,dis[now]+ls-dis[y]-z);
        if(val==-1){
            vis[now][ls]=0;return -1;
        }
        (res+=val)%=p;
    }
    vis[now][ls]=0;
    if(now==1 && ls==0) res++;
    f[now][ls]=res;
    return res;
}
void main(){
    scanf("%d%d%d%d",&n,&m,&k,&p);memset(f,-1,sizeof(f));
    for(int i=1;i<=n;i++) tu[i].clear(),ftu[i].clear();
    for(int x,y,c,i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&c);
        tu[x].push_back(node(y,c));ftu[y].push_back(node(x,c));
    }
    //spfa
    memset(dis,0x3f,sizeof(dis));memset(inq,0,sizeof(inq));
    queue<int> q;q.push(1);dis[1]=0;inq[1]=1;
    while(q.size()){
        int x=q.front();q.pop();inq[x]=0;
        for(int i=0;i<tu[x].size();i++){
            int y=tu[x][i].to,z=tu[x][i].dis;
            if(dis[y]>dis[x]+z){
                dis[y]=dis[x]+z;
                if(!inq[y]){
                    q.push(y);inq[y]=1;
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<=k;i++){
        int now=dp(n,i);
        if(now==-1){printf("-1
");return;}
        (ans+=now)%=p;
    }
    printf("%d
",ans);
    return;
}
}
int main()
{
    scanf("%d",&T);while(T--){wk::main();}
    return 0;
}
原文地址:https://www.cnblogs.com/ChrisKKK/p/11629747.html