P3953 逛公园 [dp]

逛 公 园 逛公园

题目描述见链接 .


正 解 部 分 \color{red}{正解部分}

首先 D j i Dji Dji 求出从 1 1 1 节点到所有节点的 最短路,

F [ i , j ] F[i, j] F[i,j] 表示从 1 1 1 i i i 路径长度为最短路长度加 j j j 长度的方案数,

F [ i , j ] = ∑ F [ t o , d i s i + j − w t o → i − d i s t o ] F[i, j] = \sum F[to, dis_i + j - w_{to \rightarrow i} -dis_{to}] F[i,j]=F[to,disi+jwtoidisto],


实 现 部 分 \color{red}{实现部分}

发现状态转移的顺序有些麻烦, 于是从 N N N 号节点开始 记忆化搜索,

若现在在更新 F [ i , j ] F[i, j] F[i,j], 而又搜到了 F [ i , j ] F[i, j] F[i,j], 则可以判断有 无穷解 .

注意递归边界会将包含 1 1 1 节点的 0 0 0 环判掉, 需要特殊处理 .

#include<bits/stdc++.h>
#define reg register
#define se second
#define fi first
typedef std::pair <int, int> pr;

const int maxn = 100005;
const int inf = 0x3f3f3f3f;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N;
int M;
int K;
int mod;
int num0;
int dis[maxn];
int cnt[maxn];
int head[maxn];
int F[maxn][55];

bool vis[maxn];
bool used[maxn][55];

struct Edge{ int nxt, to, w; } edge[maxn*3], E[maxn*3];

void Add(int from, int to, int w){ edge[++ num0] = (Edge){ head[from], to, w }; head[from] = num0; }

void Dij(){
        std::priority_queue <pr, std::vector<pr>, std::greater<pr> > Q;
        for(reg int i = 1; i <= N; i ++) dis[i] = inf, vis[i] = 0;
        Q.push(pr(0, 1)); dis[1] = 0;
        while(!Q.empty()){
                int ft = Q.top().se; Q.pop();
                if(vis[ft]) continue ; vis[ft] = 1;
                for(reg int i = head[ft]; i; i = edge[i].nxt){
                        int to = edge[i].to;
                        if(dis[to] > dis[ft] + edge[i].w){
                                dis[to] = dis[ft] + edge[i].w;
                                Q.push(pr(dis[to], to));
                        }
                }
        }
}

int FUCK;
bool FLAG;
int DFS(int k, int a, int d){
        if(FLAG) return 0;
        if(a > K || a < 0) return 0;
        if(used[k][a] || (FUCK && !d)) areturn FLAG=1;
        if(k == 1 && !a) return 1;
        if(~F[k][a]) return F[k][a];
        used[k][a] = 1; int s = 0;
        FUCK += dis[k]==0;
        for(reg int i = head[k]; i; i = edge[i].nxt){
                int to = edge[i].to;
                s = (s + DFS(to, dis[k]+a-edge[i].w-dis[to], d+edge[i].w)) % mod;
        }
        FUCK -= dis[k]==0;
        used[k][a] = 0;
        return F[k][a] = s;
}

void Work(){
        N = read(), M = read(), K = read(), mod = read();
        for(reg int i = 1; i <= N; i ++) head[i] = 0; num0 = 0;
        for(reg int i = 1; i <= M; i ++){
                int x = read(), y = read(), c = read();
                Add(x, y, c);
                E[i].nxt = x, E[i].to = y, E[i].w = c;
        }
        Dij();
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 0; j <= K; j ++) F[i][j] = -1;
        for(reg int i = 1; i <= N; i ++) head[i] = 0; num0 = 0;
        for(reg int i = 1; i <= N; i ++)
                for(reg int a = 0; a <= K; a ++) used[i][a] = 0;
        for(reg int i = 1; i <= M; i ++) Add(E[i].to, E[i].nxt, E[i].w);
        int Ans = 0; FLAG = 0; 
        for(reg int a = 0; a <= K && !FLAG; a ++) Ans = (Ans + DFS(N, a, 0)) % mod;
        if(FLAG) return printf("-1\n"), void();
        printf("%d\n", Ans);
}

int main(){ //多测不清空,爆零两行泪 .
           // 无解----------------------
        freopen("park.in", "r", stdin);
        freopen("park.out", "w", stdout);
        int T = read();
        while(T --) Work();
        return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值