【树形DP】战略游戏

题目来源

点我进入ACwing官网提交题目

题目描述

鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他找不到解决问题的方法,这让他很伤心。

现在他有以下问题。

他必须保护一座中世纪城市,这条城市的道路构成了一棵树。

每个节点上的士兵可以观察到所有和这个点相连的边。

他必须在节点上放置最少数量的士兵,以便他们可以观察到所有的边。

你能帮助他吗?

例如,下面的树:

1463_1.jpg.gif

只需要放置 1 名士兵(在节点 1 处),就可观察到所有的边。

输入格式
输入包含多组测试数据,每组测试数据用以描述一棵树。

对于每组测试数据,第一行包含整数 N,表示树的节点数目。

接下来 N 行,每行按如下方法描述一个节点。

节点编号:(子节点数目) 子节点 子节点 …

节点编号从 0 到 N−1,每个节点的子节点数量均不超过 10,每个边在输入数据中只出现一次。

输出格式
对于每组测试数据,输出一个占据一行的结果,表示最少需要的士兵数。

数据范围
0<N≤1500
输入样例:
4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)
输出样例:
1
2

题目思路

  • 分析题目类型

从题意上看,是基于树上进行的操作,求最小值。
那么尝试使用树形DP的思路去思考这个问题。

  • DP分析

对于每一个点,我们有两种状态,选择这个点和不选择这个点,那么就有种状态机的意思,如果我们不选择这个点,那么他的儿子节点时一定要选的,因为要遍历到每一条边,如果我们选择该点,那么他的儿子节点就有选择该点和不选择该点的两种选择,取最小值即可。

AC代码

#include<bits/stdc++.h> 
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i) 
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << ENDL
#define ENDL "\n"
#define x first 
#define y second 
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 1500 + 5, M = N * 2, INF = 0x3f3f3f3f;
int h[N], ne[M], e[M], idx, f[N][2];
bool vis[N];

void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u) {

	f[u][1] = 1, f[u][0] = 0;
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];

		dfs(j);
		f[u][0] += f[j][1];
		f[u][1] += min(f[j][1], f[j][0]);
	}
}

int main() {
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
// 	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int n;
	while (cin >> n) {
		idx = 0;
		memset(h, -1, sizeof h);
		memset(vis, 0, sizeof vis);
		while (n--) {
			int a, b, m;
			scanf("%d:(%d)", &a, &m);
			while (m--) {
				cin >> b;
				add(a, b);
				vis[b] = true;
			}
		}

		int rt = 0;
		while (vis[rt]) rt++;
		dfs(rt);

		cout << min(f[rt][1], f[rt][0]) << ENDL;
	}
	
	return 0;
} 

反思

我考虑它为树形DP,但是没想到它还结合了状态机,以后做题目要多方向尝试!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值