1. 最大子树和
题目描述:
一株奇怪的花卉,上面共连有
N
N
N朵花,共有
N
−
1
N-1
N−1条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪”之后,还剩下最后一株花(也可能是一朵)。你的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(或那朵)花卉上所有花朵的“美丽指数”之和最大。
输入格式
1.第一行一个整数
N
(
1
≤
N
≤
16000
)
N(1 ≤ N ≤ 16000)
N(1≤N≤16000)。表示原始的那株花卉上共
N
N
N朵花。
2.第二行有
N
N
N个整数,第
i
i
i个整数表示第II朵花的美丽指数。
3.接下来
N
−
1
N-1
N−1行每行两个整数
a
,
b
a,b
a,b,表示存在一条连接第
a
a
a朵花和第
b
b
b朵花的枝条。
输出格式
一个数,表示一系列“修剪”之后所能得到的“美丽指数”之和的最大值。保证绝对值不超过
2147483647
2147483647
2147483647。
由题意可知:
本题中花与花之间以枝的连接可以简化为
N
N
N个点间
N
−
1
N-1
N−1条线先来表示。而每次修剪针对的对象是线而不是点。本题所求结果为最终可得的“美丽指数”和的最大值。于是本题可以使用树形DP来解决问题。
相当于从树的根节点依次向下,取不同的子节点。而由题意知当进行“修剪”时,被剪下的一整枝都要被丢弃,即从叶节点向上递归到的当前子节点要被抛弃。
由此得转移方程:
f
[
u
]
+
=
m
a
x
(
f
[
v
]
,
0
)
f[u]+=max(f[v],0)
f[u]+=max(f[v],0)
最终可得递归到根上的结果
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int r[1000002],cnt=0,h[100000],f[100000];
struct node{
int to,next;
}edg[100000];
void add(int u,int to){
++cnt;
edg[cnt].to=to;
edg[cnt].next=h[u];
h[u]=cnt;
}
inline int qr(){
int x=0,f1=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f1=-1;c=getchar();}
while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
return x*f1;
}
void dfs(int u,int fa){
f[u]=r[u];
for(int i=h[u];i;i=edg[i].next){
int v=edg[i].to;
if(v==fa) continue;
dfs(v,u);
if(f[v]>=0) f[u]+=f[v];
}
}
int main(){
int n=qr();
for(int i=1;i<=n;++i){
r[i]=qr();
}
for(int i=1;i<n;++i){
int l=qr(),k=qr();
add(l,k);add(k,l); //无向图
}
dfs(1,0); //题意对根节点无特殊要求
printf("%d",f[1]);
return 0;
}
总结与反思:
- 剪掉的子树上所连带有具有“漂亮值”的花,不能直接剪。
- 递归终止条件不要搞错
2.没有上司的舞会
题目描述
某大学有
n
n
n个职员,编号为
1
…
n
1…n
1…n。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数
r
i
r_i
ri ,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式
输入的第一行是一个整数
n
n
n。
第二到第
(
n
+
1
)
(n+1)
(n+1)行,每行一个整数,第
(
i
+
1
)
(i+1)
(i+1)行的整数表示
i
i
i号职员的快乐指数
r
i
r_i
ri 。
第
(
n
+
2
)
(n+2)
(n+2)到第
2
n
2n
2n行,每行输入一对整数
l
,
k
l, k
l,k代表
k
k
k是
l
l
l的直接上司。
输出格式
输出一行共一个整数代表最大的快乐指数。
由题意可知:
本题以每个职员为节点,以职员与校长间的上下级关系为联系,所构成的一棵树.
且题意没有对树的有向或无向做任何规定,所以以无向处理.
再者当上司参加了舞会则下属一定不会参加.
然后输入数据中并未给出校长的标号.
可得树形背包:
f
[
u
]
[
0
]
+
=
w
a
x
(
f
[
v
]
[
0
]
,
f
[
v
]
[
1
]
)
;
f[u][0]+=wax(f[v][0],f[v][1]);
f[u][0]+=wax(f[v][0],f[v][1]);
f
[
u
]
[
1
]
+
=
f
[
v
]
[
0
]
;
f[u][1]+=f[v][0];
f[u][1]+=f[v][0];
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int r[100000],cnt=0,h[100000],f[100000][5];
struct node{
int to,next;
}edg[100000];
inline int wax(int a,int b){
return a>b?a:b;
}
void add(int u,int to){
++cnt;
edg[cnt].to=to;
edg[cnt].next=h[u];
h[u]=cnt;
}
inline int qr(){
int x=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void dfs(int u,int fa){
f[u][0]=0;
f[u][1]=r[u];
for(int i=h[u];i;i=edg[i].next){
int v=edg[i].to;
if(v==fa) continue;
dfs(v,u);
f[u][0]+=wax(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
}
int main(){
int n=qr(),rt,pre[100000];
for(int i=1;i<=n;++i){
r[i]=qr();
}
for(int i=1;i<n;++i){
int l=qr(),k=qr();
pre[l]=k; //标记每个点的前驱
add(l,k);add(k,l);
}
for(int i=1;i<=n;++i) //通过前驱判断根节点(校长)编号
if(!pre[i])
rt=i;
dfs(rt,0);
int ans=wax(f[rt][0],f[rt][1]);
printf("%d",ans);
return 0;
}
总结与反思
- 数组调太小
- 根节点无前驱
3.皇宫看守
题目描述
陆小凤成了皇上特聘的御前一品侍卫。皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状,某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入格式
输入中数据描述一棵树,描述如下:
第一行 n,表示树中结点的数目。第二行至第 n+1 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 i(0<i≤n),在该宫殿安置侍卫所需的经费 k,该边的儿子数 m,接下来 m 个数,分别是这个节点的 m 个儿子的标号r1,r2,⋯,rm 。对于一个 n 个结点的树,结点标号在 1 到 n 之间,且标号不重复。
输出格式
输出最少的经费
由题意可知:
题目中的不同宫殿为节点,不同节点的守卫对应相对的宫殿。所以其在选点时共有三种不同状态需要记录。
f
[
u
]
[
0
]
+
=
w
i
n
(
f
[
v
]
[
1
]
,
f
[
v
]
[
2
]
)
;
f[u][0]+=win(f[v][1],f[v][2]);
f[u][0]+=win(f[v][1],f[v][2]);
f
[
u
]
[
1
]
+
=
w
i
n
(
f
[
v
]
[
1
]
,
f
[
v
]
[
2
]
)
;
f[u][1]+=win(f[v][1],f[v][2]);
f[u][1]+=win(f[v][1],f[v][2]);
f
[
u
]
[
2
]
+
=
w
i
n
(
w
i
n
(
f
[
v
]
[
0
]
,
f
[
v
]
[
1
]
)
,
f
[
v
]
[
2
]
)
;
f[u][2]+=win(win(f[v][0],f[v][1]),f[v][2]);
f[u][2]+=win(win(f[v][0],f[v][1]),f[v][2]);
Code:
#include<iostream>
#include<cstdio>
#define INF 10e8
using namespace std;
int cnt=0,h[10000],f[10000][30];
int k[10000];
struct node{
int to;
int next;
}edg[10000];
inline int qr(){
int x=0,f=1;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
return x*f;
}
inline int wax(int a,int b){return a>b?a:b;}
inline int win(int a,int b){return a<b?a:b;}
void add(int u,int to){
++cnt;
edg[cnt].to=to;
// edg[cnt].val=val;
edg[cnt].next=h[u];
h[u]=cnt;
}
void dfs(int u){
int w=INF;
for(int i=h[u];i;i=edg[i].next){
int v=edg[i].to;
dfs(v);
f[u][0]+=win(f[v][1],f[v][2]);
f[u][1]+=win(f[v][1],f[v][2]);
w=win(w,f[v][2]-win(f[v][2],f[v][1]));
f[u][2]+=win(win(f[v][0],f[v][1]),f[v][2]);
}
f[u][1]+=w;
f[u][2]+=k[u];
}
int main(){
int n=qr(),dgr[10000];
for(int i=1;i<=n;++i){
int nm=qr(),m;k[nm]=qr();m=qr();
for(int j=1;j<=m;++j){
int r=qr();
dgr[r]=1;
add(nm,r);
}
} int rt;
for(int i=1;i<=n;++i){
if(!dgr[i]){
rt=i;
break;
}
}
dfs(rt);
printf("%d",win(f[rt][1],f[rt][2]));
return 0;
}
总结与反思
- 题目描述有问题,具有误导性内容(数据过大)
- 状态转移方程要进行验证,确保没有问题
- 一定要注意重复性较高代码分开写
- 不能忘掉递归终止条件