[JLOI2016/SHOI2016]侦察守卫

洛谷题目链接

动态规划:

这个题目真的看题解看了好久!!!最后还是某大佬讲了才懂。。感觉题解的解释有点错误,也可能是我太弱了。

我们设计状态

$g[i][j]$表示当前在$i$节点,处理到了第$k$棵子树时,还能向外扩展$j$层的最小花费。

$f[i][j]$表示当前在$i$节点,处理到了第$k$棵子树时,在$i$节点的子树中向下还有$j$层没有被覆盖时的最小花费。

那么状态转移(现在在点$u$,$v$为新子树):

$g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][i+1])$

意思分别是:

$u$点向上还要覆盖$j$层的话,也就是原来就可以向上覆盖$j$层,加上这棵新的子树的花费

$u$点向上还要覆盖$j$层的话,也就是这棵新子树可以向上覆盖$j+1$层,加上原来子树的花费

那么代码如下:

#include<iostream>
#include<cstdio>
#define N 500007
#define inf 0x3f3f3f3f
using namespace std;
int n,m,dis,cnt;
int val[N],head[N],f[N][21],g[N][21];
bool vis[N];
struct Edge
{
	int to,nxt;
}edge[N<<1];
void Add(int u,int v)
{
	edge[++cnt]=(Edge){v,head[u]};
	head[u]=cnt;
}
void Dfs(int u,int fa)
{
	if(vis[u])
		f[u][0]=g[u][0]=val[u];
	for(int i=1;i<=dis;++i)
		g[u][i]=val[u];
	g[u][dis+1]=inf;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)
			continue;
		Dfs(v,u);
		for(int j=0;j<=dis;++j)
			g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][j+1]);
		for(int j=dis;j>=0;--j)
			g[u][j]=min(g[u][j],g[u][j+1]);
		f[u][0]=g[u][0];
		for(int j=1;j<=dis+1;++j)
			f[u][j]+=f[v][j-1];
		for(int j=1;j<=dis+1;++j)
			f[u][j]=min(f[u][j],f[u][j-1]);
	}
}
int main()
{
	scanf("%d%d",&n,&dis);
	for(int i=1;i<=n;++i)
		scanf("%d",&val[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		int in;
		scanf("%d",&in);
		vis[in]=true;
	}		
	for(int i=1;i<n;++i)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		Add(u,v);
		Add(v,u);
	}
	Dfs(1,0);
	printf("%d",f[1][0]);
	return 0;
}

  

posted @ 2019-01-10 21:43  模拟退火  阅读(259)  评论(0编辑  收藏  举报