zoukankan      html  css  js  c++  java
  • P3953 逛公园

    题目描述

    策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。

    策策每天都会去逛公园,他总是从1号点进去,从N号点出来。

    策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。

    策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

    为避免输出过大,答案对P取模。

    如果有无穷多条合法的路线,请输出−1。

    输入格式

    第一行包含一个整数 T, 代表数据组数。

    接下来T组数据,对于每组数据: 第一行包含四个整数 N,M,K,,每两个整数之间用一个空格隔开。

    接下来M行,每行三个整数\(a_i,b_i,c_i\),代表编号为\(a_i,b_i\)​的点之间有一条权值为 \(c_i\)​的有向边,每两个整数之间用一个空格隔开。

    输出格式

    输出文件包含 T行,每行一个整数代表答案。

    输入输出样例

    输入 #1

    2
    5 7 2 10
    1 2 1
    2 4 0
    4 5 2
    2 3 2
    3 4 1
    3 5 2
    1 5 3
    2 2 0 10
    1 2 0
    2 1 0

    输出 #1

    3
    -1

    说明/提示

    【样例解释1】

    对于第一组数据,最短路为 3。 1–5,1–2–4–5,1–2–3–5为 3 条合法路径。

    【测试数据与约定】

    对于不同的测试点,我们约定各种参数的规模不会超过如下

    这个题,我们可能会想到求一遍最短路,然后直接暴力统计合法的路线数(当然了这特定会TLE啊)。

    那我们考虑怎么优化。

    一开始肯定要求一遍最短路,但我们需要的是处理出从n到所有点的路径。

    也就是建反向边跑最短路(至于为什么呢,下面会提到的)。

    我们首先考虑无解的情况。

    当存在一个零环的时候,就说明无解了。

    那有解的情况呢?

    我们可以对暴力搜索优化一下,变为记忆化搜索。

    我们用rest表示当前还能比最短路多走多少

    如上图所示,y点为n号点,x为1号点

    那么从x-z-y比从x-y的最短路多走了 dis[y]+e[i].w - dis[x]的距离(这也是我们为什么要建反边跑最短路)

    那么我们就可以得出经过每条边时多走的距离。

    接着往下搜就可以了,如果当前多走的距离要比rest大,说明此条方案不行

    若到n点之后,rest >0说明我们找到一条合法的路线,这时候直接往回推就可以了

    设 f[i][j] 为比 dis[i] 正好多 j 为长度的方案总数

    假设有一条从 p 到 now 长度为 w 的一条边

    目标:将 f[now][k] 转移到 f[p][x] ( k 为比最短路多出的长度)

    可以发现只有 x 是不知道的量。

    可以得出 x−k=dis[p]−dis[now]+w

    再将 k 移过去

    x=dis[p]+w-dis[now]+k

    dp方程就是

    f[p][x]=(f[p][x]+f[now][k]) mod p

    记忆化搜索就完事了

    不懂得同学下面有带注释的代码QAQ

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N = 1e5+10;
    int n,m,T,k,p,tot = 0,u,v,w,sum = 0;
    int head[N],dis[N],f[N][59],hed[N];
    bool vis[N][59],in[N];
    struct node{int to,net,w;}e[200010],edge[200010];
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater< pair<int,int> > >q;
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
    	return s * w;
    }
    void add(int x,int y,int w)
    {
    	e[++tot].w = w;
    	e[tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    	edge[++sum].w = w;
    	edge[sum].to = x;
    	edge[sum].net = hed[y];
    	hed[y] = sum;
    }
    void chushihua()
    {
    	tot = 0, sum = 0;
    	memset(head,0,sizeof(head));
    	memset(vis,0,sizeof(vis));
    	memset(f,0,sizeof(f));
    	memset(hed,0,sizeof(hed));
    	memset(dis,0x3f3f,sizeof(dis));
    	memset(in,0,sizeof(in));	
    }
    void dij()//在反向图上跑最短路
    {
    	q.push(make_pair(0,n)); dis[n] = 0; 
    	while(!q.empty())
    	{
    		int t = q.top().second; q.pop();
    		if(in[t]) continue;
    		in[t] = 1;
    		for(int i = hed[t]; i; i = edge[i].net)
    		{
    			int to = edge[i].to;
    			if(dis[to] > dis[t] + edge[i].w)
    			{
    				dis[to] = dis[t] + edge[i].w;
    				q.push(make_pair(dis[to],to));
    			}
    		}
    	} 
    }
    int dfs(int x,int rest)//记忆化搜索,rest表示当前还能比最短路多走的距离
    {
    	if(vis[x][rest]) return -1; //判0环 
    	if(f[x][rest]) return f[x][rest];//记忆化搜索
    	if(x == n) f[x][rest] = 1;//到达n点,说明找到一条可行的方案数
    	vis[x][rest] = 1;//打个标记
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		int tmp = dis[to] + e[i].w - dis[x];//计算走这条边要比走最短路多经过的距离
    		if(rest - tmp >= 0)//后面还能再接上 
    		{
    			int now = dfs(to,rest-tmp);//往下搜
    			if(now == -1)
    			{
    				return f[x][rest] = -1;
    			}
    			f[x][rest] =(f[x][rest] + now) % p;//f[x][rest]表示从x到n还能比最短路多走rest的方案数
    		}
    	}
    	vis[x][rest] = 0;
    	return f[x][rest]%p;
    }
    int main()
    {
    	T = read();
    	while(T--)
    	{
    		chushihua();
    		n = read(); m = read(); k = read(); p = read();
    		for(int i = 1; i <= m; i++)
    		{
    			u = read(); v = read(); w = read();
    			add(u,v,w);//建双向边
    		}
    		dij();
    		printf("%d\n",dfs(1,k));
    	}
    	return 0;
    }
    

    ENDING

  • 相关阅读:
    windows命令行下杀死进程的方法
    nodejs与javascript 笔记
    SQL Server 从一组数字中随机获取一个数
    SQL Server Default Trace查看是谁对数据库进行了DDL操作
    Default Trace 查找日志文件快速增长的原因
    使用Default Trace查看谁还原了你的数据库?
    SQL Server 默认跟踪(Default Trace)介绍使用
    (转载) SQL Server AG集群启动不起来的临时自救大招
    (转载) 搭建非域AlwaysOn win2016+SQL2016
    (转载) 从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
  • 原文地址:https://www.cnblogs.com/genshy/p/13471995.html
Copyright © 2011-2022 走看看