树形dp理解
该dp的实质就是利用树的性质,儿子节点返回的值可以被父亲节点加以利用以及处理,这样就构成了一种在树结构上的动态规划,简称树形dp。从昨天开始决定刷树形dp专题,刷了4道水题,因此想写一个博客总结一下。
POJ 1463 Strategic game
这道题就是在树上的节点添加守卫,很明显我们可以得出以下结论,如果父亲节点不添加守卫,那么儿子节点一定要添加守卫,如果父亲节点添加了守卫,那么儿子节点可以选择添加或者不添加守卫。即:
dp[fa][0]+=dp[son][1];dp[fa][1]+=min(dp[son][1],dp[son][0]);
这里我用数组的第二维表示添加守卫还是没有添加守卫,0表示没有添加,1表示添加了守卫。给出AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1505;
int tot;
int head[maxn],dp[maxn][3],vis[maxn];
struct Edge{
int to,next;
}e[maxn<<1];
void addedge(int from, int to){
e[tot].to = to;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int u){
vis[u] = 1;
dp[u][0] = 0;
dp[u][1] = 1;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(!vis[v]){
dfs(v);
dp[u][0] += dp[v][1];
dp[u][1] += min(dp[v][1], dp[v][0]);
}
}
}
int main(){
int n,m;
while(scanf("%d", &m) != EOF){
int from, to;
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
while(m--){
scanf("%d:(%d)", &from, &n);
while(n--){
scanf("%d", &to);
addedge(from, to);
addedge(to, from);
}
}
dfs(0);
printf("%d\n", min(dp[0][0], dp[0][1]));
}
return 0;
}
POJ 2378 Tree Cutting
这道题就是简单的dfs,感觉没有用到什么dp的思想,判断儿子节点数有没有超过总结点数的一半,判断除去以该点为根的子树节点数剩下的节点有没有超过总结点数的一半,这样很容易得出结论。
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10005;
struct Edge{
int to,next;
}e[maxn<<1];
int N,siz[maxn],head[maxn],vis[maxn],half,ok[maxn],tot;
vector <int> vec;
void addedge(int from,int to){
e[tot].to = to;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int now){
siz[now] = 1;
vis[now] = 1;
for(int u = head[now]; u != -1; u = e[u].next){
int v = e[u].to;
if(!vis[v]){
dfs(v);
if(siz[v] > half) ok[now] = 1;
siz[now] += siz[v];
}
}
if(N - siz[now] > half) ok[now] = 1;
}
int main(){
while(scanf("%d", &N) != EOF){
int from,to;
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(ok, 0, sizeof(ok));
memset(siz, 0, sizeof(siz));
for(int i = 1; i < N; i++){
scanf("%d%d", &from, &to);
addedge(from, to);
addedge(to, from);
}
half = N/2;
dfs(1);
for(int i = 1; i <= N; i++){
if(!ok[i]) printf("%d\n", i);
}
}
return 0;
}
POJ 3140 Contestants Division
与上题基本无差别。但是注意要用long long,并且abs函数要重写,因为long long类型不支持math.h中的abs
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 100005;
const int maxm = 1000005;
const LL INF = 0x3f3f3f3f3f3f3f3f;
struct Edge{
int to,next;
}e[maxm<<1];
int head[maxn],vis[maxn],tot;
LL MIN,total,val[maxn];
LL Abs(LL a,LL b){
if(a > b) return a-b;
else return b-a;
}
void addedge(int from, int to){
e[tot].to = to;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int u){
vis[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(!vis[v]){
dfs(v);
val[u] += val[v];
}
}
LL temp = Abs(val[u], total-val[u]);
MIN = min(MIN, temp);
}
int main(){
int N,M,kase = 1;
while(scanf("%d%d", &N, &M) != EOF){
if(!M && !N) break;
total = 0;
for(int i = 1; i <= N; i++){
scanf("%I64d", &val[i]);
total += val[i];
}
int from,to;
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= M; i++){
scanf("%d%d", &from, &to);
addedge(from, to);
addedge(to, from);
}
MIN = INF;
dfs(1);
printf("Case %d: %I64d\n", kase++, MIN);
}
return 0;
}
SGU 134 Centroid
这道题不想提了,sb题,找了半天bug,多组数据还不给过,写的一头包。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 16005;
const int INF = 0x3f3f3f3f;
int head[maxn],vis[maxn],son[maxn],siz[maxn],vec[maxn],tot,N;
struct Edge{
int to,next;
}e[maxn<<1];
void addedge(int from,int to){
e[tot].to = to;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int u){
vis[u] = 1;
siz[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(!vis[v]){
dfs(v);
siz[u] += siz[v];
if(son[u] < siz[v]) son[u] = siz[v];
}
}
son[u] = max(son[u], N-siz[u]);
}
int main(){
scanf("%d", &N);
int from,to;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(siz, 0, sizeof(siz));
memset(son, 0, sizeof(son));
memset(vec, 0, sizeof(vec));
tot = 0;
for(int i = 1; i < N; i++){
scanf("%d%d", &from, &to);
addedge(from, to);
addedge(to, from);
}
dfs(1);
int ans = INF,len = 0;
for(int i = 1; i <= N; i++){
if(son[i] < ans){
len = 0;
vec[len++] = i;
ans = son[i];
}
else if(son[i] == ans) vec[len++] = i;
}
printf("%d %d\n",ans,len);
for(int i = 0; i < len; i++)
if(i != len-1) printf("%d ", vec[i]);
else printf("%d\n", vec[i]);
return 0;
}