树形DP 小结 (jsoi 2018 潜入行动 洛谷 4516 LOJ 2546、IOI 2005 RIV 河流、POI 2014 HOT-Hotels luogu3565)

首先先看今年JSOI 2018 的DAY1T1 潜入行动,由于n*k^2的算法能够水过,所以这个DP不是非常难想,先上题面。
https://loj.ac/problem/2546 LOJ题目链接

我们可以想到的DP状态是DP[i][j][0/1][0/1]表示以i为根的子树内,我们放了j个监听器,i点是否放置监听器,i点是否能被监听到的方案数,那显然的是可以使用乘法原理类似树形背包转移,复杂度O(n*k^2),但是可以优化,我们可以使用DFS序DP 复杂度O(n*k),但是本人太蒻啦,不是很会,不多介绍了。

POI 2014 HOT-HOTELS 
题意: 有一个树形结构,每条边的长度相同,任意两个节点可以相互到达。选3个点。两两距离相等。有多少种方案?
这道题和以往见到的大多数树形DP不是很一样,通常树形DP表示的是以每一个点为根的子树内的方案,但是这道题我们通过枚举中间点来进行方案计数,我们先枚举一个点做根,然后统计所有到达这个根距离相同的三点的个数。复杂度O(n^2)
BZOJ上有加强版,做法不是很一样,需使用重链剖分。

IOI 2005 RIV 河流

题意 : 洛谷链接: https://www.luogu.org/problemnew/show/P3354
题目描述:

几乎整个Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown。

在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

注:所有的河流都不会分叉,形成一棵树,根结点是Bytetown。

国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米1分钱。

题解:我们看到这道题首先考虑设出DP状态为,DP[i][j]来表示i这棵子树里新建了j个伐木场所需要的最小运费,但是这样是错的,为什么呢?如果我们在i的不同祖先节点设置了伐木场那么i子树对答案的贡献就会发生变化,因为距离改变了,那我们考虑如何设状态呢? 我们设DP[i][j][k][0/1] 来表示i这个子树,离其最近的建了伐木场的祖先为j,子树内修建了k个伐木场,i这个节点是否建立了伐木场,最小花费是多少。然后我们就可以转移了嘻嘻嘻。方程如下,略有复杂。

for(register int j=1;j<=top;++j)
		{
			//第一维 now当前节点,第二维第一个建立伐木场的祖先,第三维子树里建立了几个伐木场,第四维now节点是否建立伐木场 
			for(register int p=k;p>=0;--p)
			{
				f[now][sta[j]][p][0]+=f[to][sta[j]][0][0];
				f[now][sta[j]][p][1]+=f[to][now][0][0];
				for(register int t=0;t<=k;++t)
				{
					if(t>p) continue;
					f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0],f[now][sta[j]][p-t][0]+f[to][sta[j]][t][0]);
					f[now][sta[j]][p][1]=min(f[now][sta[j]][p][1],f[now][sta[j]][p-t][1]+f[to][now][t][0]);
				}
			}
		}


for(register int j=1;j<=top;++j)
		{
			for(register int p=0;p<=k;++p)
			{
				if(p>=1)
				f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0]+(dep[now]-dep[sta[j]])*w[now],f[now][sta[j]][p-1][1]);
				else 
				{
					f[now][sta[j]][p][0]+=(dep[now]-dep[sta[j]])*w[now];
				}
			}
		}
	top--;
JSOI 2018 潜入行动 代码:
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
const int maxn = 2e5;
const int mod = 1e9+7;
struct node{
	int to,next;
}e[1000010];
inline int read(){int w=1,s=0;char ch=getchar();while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}return  w*s;}
int h[maxn],n,k,tot,size[maxn];
unsigned dp[maxn][110][2][2],tmp[10010][2][2];
inline void add(int from,int to){e[++tot].next=h[from],e[tot].to=to,h[from]=tot;}
void dfs(int now,int fa)
{
	size[now]=1;
	dp[now][1][1][0]=1;dp[now][0][0][0]=1;
	for(register int i=h[now];i;i=e[i].next)
	{
		int v=e[i].to;if(v==fa) continue;
		dfs(v,now);
		int minn=min(size[now],k); 
		for(register int i=0;i<=minn;++i)
		{
			tmp[i][0][0]=dp[now][i][0][0];tmp[i][1][0]=dp[now][i][1][0];tmp[i][0][1]=dp[now][i][0][1];tmp[i][1][1]=dp[now][i][1][1];
			dp[now][i][0][1]=0;dp[now][i][1][0]=0;dp[now][i][1][1]=0;dp[now][i][0][0]=0;
		}
		for(register int i=0;i<=minn;++i)
		{
			for(register int j=0,maxx=min(size[v],k-i);j<=maxx;++j)
			{
				dp[now][i+j][0][1]+=(long long)(1ll*(dp[v][j][0][1]+dp[v][j][1][1])%mod*tmp[i][0][1]%mod+1ll*tmp[i][0][0]*(dp[v][j][1][1])%mod)%mod;
				dp[now][i+j][0][1]%=mod;
				dp[now][i+j][1][1]+=(long long )(1ll*tmp[i][1][1]*(dp[v][j][0][1]+dp[v][j][1][1]+dp[v][j][0][0]+dp[v][j][1][0])%mod+1ll*tmp[i][1][0]*(dp[v][j][1][1]+dp[v][j][1][0])%mod)%mod;
				dp[now][i+j][1][1]%=mod;
				dp[now][i+j][0][0]+=(long long)(1ll*tmp[i][0][0]*dp[v][j][0][1]%mod);
				dp[now][i+j][0][0]%=mod;
				dp[now][i+j][1][0]+=(long long )(1ll*tmp[i][1][0]*(dp[v][j][0][1]+dp[v][j][0][0])%mod)%mod;
				dp[now][i+j][1][0]%=mod;
			}
		}
		size[now]+=size[v];
	}
}
int main(){
	n=read();k=read();
	for(register int i=1;i<n;++i)
	{
		int x,y;x=read(),y=read();add(x,y);add(y,x);
	}
	dfs(1,0);
	cout<<(dp[1][k][1][1]+dp[1][k][0][1])%mod;
	return 0;
}

IOI 2005 河流 代码:
// luogu-judger-enable-o2
#include<bits/stdc++.h>

using namespace std;
const int maxn =  3e2;
long long  n,w[maxn],h[maxn],k,tot,f[maxn][maxn][maxn][2],fa[maxn],dep[maxn],sta[maxn],top;
struct node{
	long long to,next,w;
}e[maxn<<2];
inline void add(int from,int to,int w){e[++tot].next=h[from],e[tot].to=to,h[from]=tot,e[tot].w=w;}
void dfs(int now)
{
	sta[++top]=now;
	for(register int i=h[now];i;i=e[i].next)
	{
		int to=e[i].to;dep[to]=dep[now]+e[i].w;dfs(to);
		for(register int j=1;j<=top;++j)
		{
			//第一维 now当前节点,第二维第一个建立伐木场的祖先,第三维子树里建立了几个伐木场,第四维now节点是否建立伐木场 
			for(register int p=k;p>=0;--p)
			{
				f[now][sta[j]][p][0]+=f[to][sta[j]][0][0];
				f[now][sta[j]][p][1]+=f[to][now][0][0];
				for(register int t=0;t<=k;++t)
				{
					if(t>p) continue;
					f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0],f[now][sta[j]][p-t][0]+f[to][sta[j]][t][0]);
					f[now][sta[j]][p][1]=min(f[now][sta[j]][p][1],f[now][sta[j]][p-t][1]+f[to][now][t][0]);
				}
			}
		}
	}
	for(register int j=1;j<=top;++j)
		{
			for(register int p=0;p<=k;++p)
			{
				if(p>=1)
				f[now][sta[j]][p][0]=min(f[now][sta[j]][p][0]+(dep[now]-dep[sta[j]])*w[now],f[now][sta[j]][p-1][1]);
				else 
				{
					f[now][sta[j]][p][0]+=(dep[now]-dep[sta[j]])*w[now];
				}
			}
		}
	top--;
}
int main()
{
	cin>>n>>k;
	for(register int i=1,u;i<=n;++i)
	{
		int v;
		cin>>w[i]>>fa[i]>>v; add(fa[i],i,v);
	}
	dfs(0);
	cout<<f[0][0][k][0];
	return 0;
}

POI 2014 HOT-hotels
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define ll long long
using namespace std;
struct node{
	int to,next;
}e[1000010];
inline int read(){int w=1,s=0;char ch=getchar();while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}return  w*s;}
ll f[5050],tot,h[1000010],n,m,g[5050],ans,dep[5001],tmp[5010],maxdep;
inline void add(int from,int to){e[++tot].next=h[from];h[from]=tot;e[tot].to=to;}
void dfs(int now,int fa)
{
	tmp[dep[now]]++;
	maxdep=max(maxdep,dep[now]);
	for(register int i=h[now];i;i=e[i].next)
	{
		int to=e[i].to; if(to==fa) continue; dep[to]=dep[now]+1;
		dfs(to,now); 
	}
}
inline void calc()
{
	for(register int i=1;i<=maxdep;++i)
	{
		ans+=g[i]*tmp[i];
		g[i]+=tmp[i]*f[i];
		f[i]+=tmp[i];
	}
	for(register int i=1;i<=maxdep;++i) tmp[i]=0;
}
int main()
{
	n=read();
	for(register int u,v,i=1;i<n;++i) u=read(),v=read(),add(u,v),add(v,u);
	for(register int now=1;now<=n;++now){memset(f,0,sizeof(f));memset(g,0,sizeof(g));for(register int i=h[now];i;i=e[i].next){int to=e[i].to;dep[to]=1;dfs(to,now);calc();}}
	cout<<ans;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/was__n/article/details/80542815