动态规划·树形DP·题解【战略游戏】

题目

WOJ#2532 战略游戏
洛谷P2016 战略游戏
这道题主要是看边的情况。

题目描述

Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。
他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能瞭望到所有的路。
注意:某个士兵在一个结点上时,与该结点相连的所有边将都可以被瞭望到。
请你编一个程序,给定一棵树,帮Bob计算出他需要放置最少的士兵数。

输入格式

第一行 N,表示树中结点的数目。
第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连)。
接下来k个数,分别是每条边的另一个结点标号r1,r2,…,rk。
对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入数据中每条边只出现一次。

输出格式

输出文件仅包含一个数,为所求的最少的士兵数目。

样例

  • 样例输入
    4
    0 1 1
    1 2 2 3
    2 0
    3 0
  • 样例输出
    1

题意

在一棵树上取若干个点,使得所有边都被连结。求点的个数的最小值。

思路

对于这类最值问题,应当用动态规划求解。
s s s是树的根,则设 f [ s ] f[s] f[s]表示求以 s s s为根的子树上需要安放的最少士兵数量。我们希望用 s s s的儿子的对应信息来推出 f [ s ] f[s] f[s]
s s s有放和不放两种可能。当 s s s节点放时,它的子节点放和不放都可以;当 s s s节点不放时,它的子节点必须放。所以需要加维来表示树的根结点是否选取,才能从子树推算出父亲的最大值。

f [ i , 1 ] f[i,1] f[i,1]表示 i i i点放士兵时,以 i i i为根的子树需要的最少士兵数目; f [ i , 0 ] f[i,0] f[i,0]表示 i i i点不放士兵时,以 i i i为根的子树需要的最少士兵数目。

  1. 当点 i i i不放时,则它的所有儿子都必须放;
    f [ i ] [ 0 ] = ∑ f [ j ] [ 1 ] f[i][0]=∑f[j][1] f[i][0]=f[j][1];其中 j j j i i i的儿子。
  2. 当点 i i i放时,则它的所有儿子放与不放无所谓,但应该取两种情况的最小值;
    f [ i , 1 ] = ∑ m i n ( f [ j ] [ 1 ] , f [ j ] [ 0 ] ) f[i,1]=∑min(f[j][1],f[j][0]) f[i,1]=min(f[j][1],f[j][0]);其中 j j j i i i的儿子

初始条件:
f [ i ] [ 0 ] = 0 , f [ i , 1 ] = 1 f[i][0]=0,f[i,1]=1 f[i][0]=0,f[i,1]=1
a n s = m i n f [ s ] [ 0 ] , f [ s ] [ 1 ] ans=min{f[s][0],f[s][1]} ans=minf[s][0],f[s][1]

代码

#include<iostream>
#include<cstdio>
#include<cctype>
using namespace std;
const int maxn=1510;
int n,s,cnt=2,ans=0;
int first[maxn],f[maxn][2];
struct edge {int u,v,w,nxt;};
edge e[maxn<<1];
inline int read()
{
	int x=0;bool f=0;char c=getchar();
	while(!isdigit(c)) {f|=c=='-';c=getchar();}
	while(isdigit(c)) {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return f?-x:x;
}
inline void add(int u,int v)
{
	e[cnt].u=u;e[cnt].v=v;
	e[cnt].nxt=first[u];first[u]=cnt++;
}
inline void dfs(int x,int fa)
{
	f[x][0]=0,f[x][1]=1;
	for(int i=first[x];i;i=e[i].nxt)
	{
		int y=e[i].v;
		if(y==fa) continue;
		dfs(y,x);
		f[x][1]+=min(f[y][0],f[y][1]);
		f[x][0]+=f[y][1];
	}
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		int idx=read(),k=read();
		for(int j=1;j<=k;j++)
		{
			int r=read();
			add(idx,r);
			add(r,idx);
		}
	}
	s=1;
	dfs(0,-1);
	ans=min(f[0][0],f[0][1]);
	cout<<ans;
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值