POJ1463 Strategic game(树形DP)

传送门
http://poj.org/problem?id=1463

这是一道树形DP入门题。

分析符号说明

  • node_num:结点数
  • edge_num:边数
  • saved_edge_num:无向图中存储的边的数量
  • dp[][]:动态规划数组dp[i][j]表示第i的结点是(j=1)否(j=0)有兵的情况下其与子结点的所需兵的最小值
  • x:父结点
  • y:子结点

分析

  • 由题目中the roads of which form a tree知道路是呈树形的【这一点很重要,如果不是树,下面的AC代码会导致无限DFS爆栈,文末我会给出一个简单的例子说明】任意一个结点作为根结点都可以。
  • 我存的是无向图,由树,知
    edge_num = node_num - 1
    因此在存道路的时候存边的次数满足
    0 < saved_edge_num ≤ 2 * ( 1500 - 1 )
  • 状态转移方程
    1. 父结点有兵,那么子结点可以有兵也可以没有兵,所以有
      dp[x][1] = Σ min{ dp[y][0], dp[y][1] }
    2. 父结点无兵,那么子结点必须有兵,所以有
      dp[x][0] = Σ dp[y][1]

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N = 1505;

int node_num;//结点数
int nodeid, current_node_num, anothernode;//当前结点id,当前结点所连结点树,某一其他结点
int t[N*2], ne[N*2], h[N], idx;//存图
int dp[N][2];//dp数组

inline int smaller(const int &a,const int &b) {
	return a < b ? a : b;
}


inline void addEdge(int be,int en) {
	t[idx] = en;
	ne[idx] = h[be];
	h[be] = idx++;
}


void dfs_dp(int x,int fa) {//树形DP
	dp[x][1] = 1;
	dp[x][0] = 0;
	for (int i = h[x]; i != -1; i = ne[i]) {
		if (t[i] == fa)
			continue;
		dfs_dp(t[i], x);
		dp[x][1] += smaller(dp[t[i]][0], dp[t[i]][1]);
		dp[x][0] += dp[t[i]][1];
	}
}


int main() {
	while (scanf("%d", &node_num) != EOF) {
		memset(h, -1, sizeof(h));
		idx = 0;

		for (int i = 0; i < node_num; ++i) {
			scanf("%d:(%d)", &nodeid, &current_node_num);
			for (int j = 0; j < current_node_num; ++j) {
				scanf("%d", &anothernode);
				addEdge(nodeid, anothernode);
				addEdge(anothernode, nodeid);
			}
		}
		dfs_dp(0, -1);//0作为根结点
		printf("%d\n", smaller(dp[0][1], dp[0][0]));
	}
	return 0;
}

ps:如果是图

以下为一个图(非树)的道路的输入例子:

3
0:(2) 1 2
1:(1) 2
2:(0)

在这里插入图片描述
在这个情况下DFS递归的时候就会出现问题了,上图中会按照顺时针永远DFS递归运行,无法正常结束,直至出现爆栈异常被终止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值