P3953 逛公园-spfa,dijkstra,记忆化搜索

版权声明:原创,勿转 https://blog.csdn.net/qq_42386465/article/details/82556351

https://www.luogu.org/problemnew/show/P3953

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

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

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

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

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

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

看到这道题首先想到的是肯定要使用最短路算法求解。30分做法只需跑一边spfa,因为k=0,所以之后最短路计数即可;这道题看到k<=50,应该能想到是一道与k有关的dp,那么状态可以设为f(i,k),表示在i这个点还能多走k长度。建图时用正反都建图,先正向跑堆优化dijkstra,计算出每一个点到1的最短距离dis数组。因为不是每个点都能到达终点,这样再反向跑spfa,计算出哪些点与终点n联通即可,即used数组。再dfs中,使用vis数组判断是否有0环。这道题就这样被解决了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int inline read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=100050,M=200050,K=65,inf=0x3f;
int T,n,m,k,p,cntz,cntn;
int headz[N],headn[N],ans[N][K],dis[N];
bool used[N],vis[N][K];
struct node2
{
	int u,d;
	bool operator < (const node2& rhs)const{
		return d>rhs.d;
	}
};
priority_queue<node2> q;
queue<int> f;
struct node
{
	int next,to,w;
}ez[M],en[M];
void addz(int x,int y,int z)
{
	ez[++cntz].next=headz[x];
	ez[cntz].to=y;
	ez[cntz].w=z;
	headz[x]=cntz;
}
void addn(int x,int y,int z)
{
	en[++cntn].next=headn[x];
	en[cntn].to=y;
	en[cntn].w=z;
	headn[x]=cntn;
}
void zheng_dijkstra()
{
	dis[1]=0;
	q.push((node2){1,0});
	while(!q.empty()){
		node2 now=q.top();q.pop();
		int u=now.u,d=now.d;
		if(d!=dis[u]) continue;
		for(int i=headz[u];i;i=ez[i].next){
			int v=ez[i].to;
			if(dis[v]>dis[u]+ez[i].w){
				dis[v]=dis[u]+ez[i].w;
				q.push((node2){v,dis[v]});
			}
		}
	}
	return;
}
void ni_spfa()
{
	f.push(n);
	used[n]=1;
	while(!f.empty()){
		int u=f.front();f.pop();
		for(int i=headn[u];i;i=en[i].next){
			int v=en[i].to;
			if(!used[v]){
				used[v]=1;
				f.push(v);
			}
		}
	}
	return;
}
int dfs(int a,int b)
{
	if(b<0) return 0;
	else if(vis[a][b]==1) return -inf;
	else if(ans[a][b]!=-1) return ans[a][b];
	else{
		vis[a][b]=1;
		int key=0;
		if(a==n){
			key++;
		}
		for(int i=headz[a];i;i=ez[i].next){
			int v=ez[i].to,w=ez[i].w,d=dis[v]-dis[a];
			if(!used[v]) continue;
			int sum=dfs(v,b-(w-d));
			if(sum==-inf){
				return -inf;
			}
			else{
				key=(key+sum)%p;
			}
		}
		vis[a][b]=0;
		ans[a][b]=key%p;
		return key;
	}
}
void init()
{
	memset(ez,0,sizeof(ez));
	memset(en,0,sizeof(en));
	memset(headz,0,sizeof(headz));
	memset(headn,0,sizeof(headn));
	memset(dis,inf,sizeof(dis));
	memset(used,false,sizeof(used));
	memset(vis,false,sizeof(vis));
	memset(ans,-1,sizeof(ans));
}
int main()
{
	T=read();
	while(T--){
		init();
		n=read();m=read();k=read();p=read();
		for(int i=1;i<=m;++i){
			int x,y,z;
			x=read();y=read();z=read();
			addz(x,y,z);addn(y,x,z);
		}
		zheng_dijkstra();
		ni_spfa();
		int tot=dfs(1,k);
		if(tot==-inf){
			cout<<-1<<endl;
		}
		else{
			cout<<tot<<endl;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42386465/article/details/82556351
今日推荐