[记忆化DFS]NOIP2017Day1T3 逛公园 题解

这篇博客主要介绍了NOIP2017Day1T3题目——逛公园的解题思路,重点探讨了如何利用记忆化深度优先搜索(DFS)解决该问题。博主指出,由于题目限制k≤50,可以设置动态规划状态f[i][j]表示到达i点且路径长度比最长路长j的情况。初始求最短路,之后进行转移。文中提到可以用拓扑+DP或记忆化DFS,但记忆化DFS更便于实现。博主提供了反向建图的转移公式,并解释了如何判断0环的存在,即当某个状态出现两次时,表明存在0环。
摘要由CSDN通过智能技术生成

解题分析

题面网上肯定找得到,不贴了……

仍然填大坑ing……

当初在考场上看到了此题,由于发现 k ≤ 50 k\le50 k50,所以自然要在 k k k上搞事情。设 f [ i ] [ j ] f[i][j] f[i][j]表示到达i点此时的路径总长度比最长路长j的题解,初始最短路求一趟,然后转移……转移……我又写了个spfa神奇转移骗走60分……

这道题可以考虑拓扑+DP转移,然而也可以记忆化DFS转移,由于记忆化DFS转移方便,所以各位可以在网上找有关DP+拓扑的解法。

当初转移的时候是

f [ s o n [ j ] ] [ k + w + d s t [ x ] − d s t [ s o n [ j ] ] ] + = f [ x ] [ k ] f[son[j]][k+w+dst[x]-dst[son[j]]]+=f[x][k] f[son[j]][k+w+dst[x]dst[son[j]]]+=f[x][k]

但其实可以反过来,反向建图一波。

f [ x ] [ k ] = ∑ f [ s o n [ j ] ] [ k − w [ j ] + d s t [ x ] − d s t [ s o n [ j ] ] ] f[x][k]=\sum f[son[j]][k-w[j]+dst[x]-dst[son[j]]] f[x][k]=f[son[j]][kw[j]+dst[x]dst[son[j]]]

然后判断无数个解,即图中存在0环,其实在记忆化搜索的过程中某个状态出现了两次就说明出现了 0 环。

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005,maxe=400005;
int n,e,K,tot,tt,tst,ans,dst[maxn],lnk[2][maxn],nxt[maxe],son[maxe],w[maxe],f[maxn][55],que[maxn];
bool vs[maxn][55],vis[maxn],pd;
inline char nc(){
	static char buf[100000],*pa=buf,*pb=buf;
	return pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++;
}
inline void readi(int &x){
	x=0; char ch=nc();
	while ('0'>ch||ch>'9') ch=nc();
	while ('0'<=ch&&ch<='9'){x=x*10+ch-'0'; ch=nc();}
}
void _add(int t,int x,int y,int z){son[++tot]=y; w[tot]=z; nxt[tot]=lnk[t][x]; lnk[t][x]=tot;}
void _init(){
	readi(n); readi(e); readi(K); readi(tt); tot=0;
	memset(lnk,0,sizeof(lnk));
	for (int i=1,x,y,z;i<=e;i++){
		readi(x); readi(y); readi(z);
		_add(0,x,y,z); _add(1,y,x,z);
	}
}
void _spfa(){
	memset(vis,0,sizeof(vis));
	memset(dst,63,sizeof(dst));
	int hed=0,til=1; que[1]=1; vis[1]=1; dst[1]=0;
	while (hed!=til){
		hed=(hed+1)%maxn; vis[que[hed]]=0;
		for (int j=lnk[0][que[hed]];j;j=nxt[j])
			if (dst[son[j]]>dst[que[hed]]+w[j]){
				dst[son[j]]=dst[que[hed]]+w[j];
				if (!vis[son[j]]){
					til=(til+1)%maxn; que[til]=son[j]; vis[son[j]]=1;
					if (dst[son[j]]<dst[que[(hed+1)%maxn]]) swap(que[til],que[(hed+1)%maxn]);
				}
			}
	}
}
int _dfs(int x,int k){
	if (!pd) return -1; if (f[x][k]!=-1) return f[x][k]; f[x][k]=0; vs[x][k]=1;
	for (int j=lnk[1][x];j;j=nxt[j]){
		int now=k-(w[j]-dst[x]+dst[son[j]]); if (now<0) continue;
//		printf("%d %d %d %d\n",x,son[j],now,k);
		if (vs[son[j]][now]){pd=0; return -1;} else f[x][k]=(f[x][k]+_dfs(son[j],now))%tt;
	}
	vs[x][k]=0; return f[x][k];
}
void _solve(){
	_spfa();
	memset(f,255,sizeof(f));
	memset(vs,0,sizeof(vs));
	pd=1; ans=0; f[1][0]=1; _dfs(n,1);
	for (int i=0;pd&&i<=K;i++) ans=(ans+_dfs(n,i))%tt;
	if (!pd) ans=-1; printf("%d\n",ans);
}
int main()
{
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
	readi(tst);
	while (tst--){
		_init();
		_solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值