[記憶化DFS]NOIP2017Day1T3 逛公園 題解

解題分析

題面網上肯定找得到,不貼了……

仍然填大坑ing……

當初在考場上看到了此題,由於發現k50k\le50,所以自然要在kk上搞事情。設f[i][j]f[i][j]表示到達i點此時的路徑總長度比最長路長j的題解,初始最短路求一趟,然後轉移……轉移……我又寫了個spfa神奇轉移騙走60分……

這道題可以考慮拓撲+DP轉移,然而也可以記憶化DFS轉移,由於記憶化DFS轉移方便,所以各位可以在網上找有關DP+拓撲的解法。

當初轉移的時候是

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[son[j]][kw[j]+dst[x]dst[son[j]]]f[x][k]=\sum f[son[j]][k-w[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章