HDU_1054 树形DP水题(树形DP小结)
给树形DP写给小结:树形DP大致就是在在树上找递推关系。对于节点i:找其父节点fa,其子节点j之间的关系(就是向上推和向下推的做法)。一般的题目就是找子节点的关系:HDU_1054,,ZOJ_2834,HDU_1520,同时找父节点和子节点的HDU_2196。其实应该怎么找,题目读完了基本就知道了。其次还有的就是该节点的状态:dp[i][N]代表这个节点有N种状态,一种状态的好像比较少,两种状态的HDU_1054,HDU_1520,HDU_2196,三种状态的:ZOJ_2834。基本就是这些了。。。下面来说这道题:
直接给状态转移方程:
dp[u][0] = sum{dp[v][1]}; 代表u节点没有士兵
dp[u][1] =1+sum{dp[v][1],dp[v][0]}; 代表u节点有士兵
下面贴代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> #include <vector> #define ll long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define INF 1111111111 using namespace std; const int MAXN = 1600; //dp[i]代表第i个节点以及其子树的所有节点都被覆盖所需要的最少士兵数 int n,dp[MAXN][2]; //dp[i][0]代表第i个点没有士兵,dp[i][1]代表第i个点有士兵 vector <int> G[MAXN]; void dfs(int u,int fa){ vector <int> :: iterator it; dp[u][0] = 0; dp[u][1] = 1; int d_value = INF; for(it = G[u].begin();it != G[u].end();it ++){ int v = *it; if(v == fa) continue; dfs(v,u); d_value = min(d_value,dp[v][1]-dp[v][0]); dp[u][0] += dp[v][1]; dp[u][1] += min(dp[v][0],dp[v][1]); } } void init_input(){ FOR(i,0,n) G[i].clear(); int u,v,cnt; char str[30]; FOR(i,0,n){ scanf("%s",str); u = atoi(str); int len = strlen(str); FOR(i,0,len) if(str[i] == '(') {cnt = atoi(str+i+1); break;} FOR(i,0,cnt){ scanf("%d",&v); G[u].push_back(v); G[v].push_back(u); } } } int main() { while(~scanf("%d",&n)){ init_input(); dfs(0,-1); if(n==1) printf("1\n"); else printf("%d\n",min(dp[0][0],dp[0][1])); } return 0; }