树型DP[专题]
一、经典题系列。
1.选课
简要描述:从n门课中选出m门课(m<=n),每一门课有一门(或没有)先修课,每门课都有对应的学分,求最大学分。
分析:n门课程按照是否是先修的关系构成一个森林(即树型),设计状态d[i][j]:以结点i为根的子树选j门课能得到的最大学分值。根结点的状态由孩子结点得到,为了状态转移的方便,将多叉树转换成二叉树(孩子-兄弟表示法),这样孩子结点就变成了两个,转移方程是:
d[i][j] = max(d[ right[i] ][j], max( d[ left[i] ][k] + d[ right[i] ][j-k-1] + scr[i] )(0<=k<j))
需要思考的有两点:
1.右孩子表示的是根结点的兄弟结点,那么状态d[i][j]的意义跟最初的意义不同了,d[i][j]:表示将k门课分给 i 的孩子结点,将 j-i-1门课分给一个兄弟结点,但是,这两个结点是转换成二叉树后 i 的左右孩子结点,想想为什么这样是对的?
2. 转移方程中用红色标记的是边界情况,即结点 i 分到了 0 门课,j 门课都分给了兄弟结点。
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define SUPERBIN
#define NL1 311
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long
int d[NL1][NL1], left[NL1], right[NL1], scr[NL1];
int n, m;
int max(int x, int y)
{
return x>y?x:y;
}
void DFS(int u) {
int i, j, k;
if (left[u] > 0) DFS(left[u]);
if (right[u] > 0) DFS(right[u]);
for (i = 1; i <= m; i++) {
for (j = 0; j < i; j++) {
d[u][i] = max(d[u][i], d[left[u]][j] + d[right[u]][i - j - 1] + scr[u]);
// printf("d[%d][%d] = %d\n", u, i, d[u][i]); //test
}
d[u][i] = max(d[u][i], d[right[u]][i]);
}
}
int main() {
int T, cs = 1;
int i, j, k, a, b, c, x, y, z;
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++) {
scanf("%d%d", &a, &scr[i]);
right[i] = left[a]; //多叉转二叉
left[a] = i; //多叉转二叉
}
DFS(left[0]);
printf("%d\n", d[left[0]][m]);
return 0;
}
类似的题:
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
//#define SUPERBIN
#define NL1 201
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long
int d[NL1][NL1], ch[NL1], sib[NL1], v[NL1], ct[NL1];
int n, m;
void DFS(int k)
{
if (ch[k]) DFS(ch[k]);
if (sib[k]) DFS(sib[k]);
d[k][1] = v[k];
int i, j;
for (i=1; i<=m; i++) {
for (j=0; j<i; j++) {
d[k][i] = MAX(d[k][i], d[ch[k]][j]+d[sib[k]][i-j-1]+v[k]);
}
d[k][i] = MAX(d[k][i], d[sib[k]][i]);
}
}
int main() {
#ifdef SUPERBIN
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T, t, r, cs = 1;
int i, j, k, a, b, c, x, y, z;
while (scanf("%d%d", &n, &m) != EOF) {
if (!n && !m) break;
memset(ch, 0, sizeof(ch));
memset(sib, 0, sizeof(sib));
memset(d, 0, sizeof(d));
for (i=1; i<=n; i++) {
scanf("%d%d", &a, &v[i]);
sib[i] = ch[a];
ch[a] = i;
}
DFS(ch[0]);
printf("%d\n", d[ch[0]][m]);
}
return 0;
}
/*
* 同‘选课’,跑了250ms有待改进
*/
2.Anniversary Party
简要描述:公司要举行宴会,要求被邀请的员工不能有直接上司-下属关系。
分析:对于结点 i ,分邀请和不邀请两种情况,设d[i][0]表示不邀请,d[i][1]表示邀请,则:
d[i][0] = Sum(max(d[j][0], d[j][1])(j是i的孩子结点)),
d[i][1] = Sum(d[j][0])。
PS:实现的时候还是转换成二叉树。
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define SUPERBIN
#define NL1 6011
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long
int d[NL1][2], left[NL1], right[NL1], v[NL1];
bool flg[NL1];
int n;
int max(int x, int y) {
return x > y ? x : y;
}
void DFS(int u) {
int son = left[u];
d[u][1] = v[u];
// printf("u = %d\n", u); //test
while (son > 0) {
// printf("son = %d\n", son); //test
DFS(son);
d[u][0] += MAX(d[son][1], d[son][0]);
d[u][1] += d[son][0];
// printf("d[%d][0] = %d, d[%d][1] = %d\n", u, d[u][0], u, d[u][1]); //test
son = right[son];
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
int T, cs = 1;
int i, j, k, a, b, c, x, y, z;
while (scanf("%d", &n) != EOF) {
v[0] = 0;
for (i = 1; i <= n; i++)
scanf("%d", &v[i]);
memset(left, -1, sizeof (left));
memset(right, -1, sizeof (right));
memset(d, 0, sizeof (d));
memset(flg, 0, sizeof (flg));
while (scanf("%d%d", &a, &b)) {
if (!a && !b) break;
right[a] = left[b];
left[b] = a;
flg[a] = 1;
}
for (i = 1; i <= n; i++) {
if (!flg[i]) {
right[i] = left[0];
left[0] = i;
}
}
DFS(0);
printf("%d\n", MAX(d[0][0], d[0][1]));
}
return 0;
}
二、较复杂
1.computer
简要描述:n个电脑组成一个树形的网络,求各个电脑跟其他电脑通信的最远距离。
分析:在网上参考了一些解题报告,但感觉讲的都不太明白,不过还是想通了。对于某个结点 i ,它跟其他结点的距离可以划分成三部分,见下图:
d[i]表示:以 i 为根的子树,i 的子孙结点到 i 的最长距离,
d1[i]表示:以 i 的兄弟结点为根的子树,其子孙结点到 i 的父结点的最长距离,
d2[i]表示:i 的父结点 以上(即除去i的父结点为根的子树)的结点到 父结点的最长距离。
则 f[i] = max( d[i], max(d1[i], d2[i])+dis[i] )
ps:以上的符合表示的含义可能跟程序中的有出入。
还是喜欢用孩子-兄弟表示法,O(∩_∩)O~
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
//#define SUPERBIN
#define NL1 10001
#define NL2 21
#define EP 1e-10
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#define LL long long
int ch[NL1], sib[NL1], p[NL1], dis[NL1], d[NL1], cd[NL1], d1[NL1], d2[NL1];
int n;
void DFS_1(int k) {
int k0 = ch[k], t;
while (k0) {
DFS_1(k0);
t = d[k0] + dis[k0];
if (t > d[k]) {
d1[k] = d[k];
d[k] = t;
cd[k] = k0;
}
k0 = sib[k0];
}
k0 = ch[k];
while (k0) {
t = d[k0] + dis[k0];
if (k0 != cd[k] && d1[k]<t) {
d1[k] = t;
}
k0 = sib[k0];
}
}
void DFS_2(int k) {
int k1 = ch[k];
while (k1) {
if (k1 == cd[k]) {
d2[k1] = MAX(d2[k], d1[k]) + dis[k1];
} else {
d2[k1] = MAX(d2[k], d[k]) + dis[k1];
}
DFS_2(k1);
k1 = sib[k1];
}
}
int main() {
#ifdef SUPERBIN
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T, t, r, cs = 1;
int i, j, k, a, b, c, x, y, z;
while (scanf("%d", &n) != EOF) {
memset(ch, 0, sizeof (ch));
memset(sib, 0, sizeof (sib));
memset(p, 0, sizeof (p));
memset(d, 0, sizeof (d));
memset(d1, 0, sizeof (d1));
memset(d2, 0, sizeof (d2));
for (i = 2; i <= n; i++) {
scanf("%d%d", &a, &dis[i]);
sib[i] = ch[a];
ch[a] = i;
}
DFS_1(1);
DFS_2(1);
/* for (i=1; i<=n; i++) printf("d[%d] = %d\n", i, d[i]); //test
for (i=1; i<=n; i++) printf("cd[%d] = %d\n", i, cd[i]); //test
for (i=1; i<=n; i++) printf("d1[%d] = %d\n", i, d1[i]); //test
for (i=1; i<=n; i++) printf("d2[%d] = %d\n", i, d2[i]); //test
*/
for (i = 1; i <= n; i++) {
printf("%d\n", MAX(d[i], d2[i]));
}
}
return 0;
}
转载于:https://www.cnblogs.com/superbin/archive/2010/10/03/1841682.html