我是菜鸡,我是菜鸡,我是菜鸡。。。。重要的事说三遍

算是第一次做树形dp的题吧,不太难。。

园林构成一棵树,root为1,Hi从root出发,有k个园林必须玩,每个园林游玩后会得到权值w[i],最多玩M个园林。

经过的园林必须玩,问可得到的最大权值和。

题目链接:http://hihocoder.com/problemset/problem/1104

经典的树形dp,dp[i][j],表示以i为根的子树选j个结点可得的最大权值和。

这样dp[root][num] = g[son_num][num-1] + w[root];

g[son_num][num-1]类似于对root的儿子结点的子树做背包,g[i][j] = max( g[i-1][j-p] + dp[child_id][p] );

题解不错:http://hihocoder.com/discuss/question/2743

对于必须玩的k个点,他的处理方法我没有看懂。。。。discussion里的不错。。。游玩结点i,那么其祖先结点fa[i]必要也游玩,将这些点设为must,

将must构成的子树缩成一个点,rebuild a new tree,就成了上面说的最普通的树形dp了。

 #include<bits/stdc++.h>

 using namespace std;
const int maxn = + ;
int n, k, m; vector<int> G[maxn], G1[maxn];
int fa[maxn], w[maxn], must[maxn], vis[maxn];
int f[maxn][maxn]; void init(){
for( int i = ; i < maxn; ++i ){
G[i].clear();
G1[i].clear();
}
memset( fa, -, sizeof(fa) );
memset( f, -, sizeof(f) );
memset( must, , sizeof(must) );
} void dfs( int u, int pa ){
fa[u] = pa;
for( int i = ; i < G[u].size(); ++i ){
int v = G[u][i];
if( v == pa )
continue;
dfs( v, u );
}
} void dfs1( int u, int pa ){
if( pa != - ){
if( vis[pa] && !vis[u] )
G1[].push_back(u);
else if( !vis[pa] && !vis[u] )
G1[pa].push_back(u);
} for( int i = ; i < G[u].size(); ++i ){
int v = G[u][i];
if( v == pa )
continue;
dfs1( v, u );
}
} int dp( int root, int num ){
if( num == )
return ;
if( f[root][num] != - )
return f[root][num]; int g[maxn][maxn], son_num = G1[root].size();
memset( g, , sizeof(g) );
for( int i = ; i <= son_num; ++i ){
int child = G1[root][i-];
for( int j = ; j < num; ++j ){
for( int p = ; p <= j; ++p ){
g[i][j] = max( g[i][j], g[i-][j-p] + dp( child, p ) );
}
}
} f[root][num] = g[son_num][num-] + w[root];
return f[root][num];
} void print( int num ){
for( int i = ; i < G1[num].size(); ++i )
cout << G1[num][i] << " ";
cout << endl;
} void solve(){
dfs(, -); //must节点压缩
int ans = , cnt = ;
memset( vis, , sizeof(vis) );
for( int i = ; i <= n; ++i ){
if(must[i]){
int u = i;
while(u != - && !vis[u]){
vis[u] = ;
cnt++, ans += w[u];
u = fa[u];
}
}
}
//cout << "ans: " << ans << endl; if( cnt > m ){
printf("-1\n");
return;
} //rebuild tree
dfs1( , - ); w[] = ans;
//cout << "m-cnt: " << m - cnt << endl;
ans = dp( , m-cnt+ );
//cout << "f: " << f[8][3] << endl; printf("%d\n", ans);
} int main(){
//freopen("1.in", "r", stdin);
init();
scanf("%d%d%d", &n, &k, &m);
for( int i = ; i <= n; ++i ){
scanf("%d", &w[i]);
} int t;
for( int i = ; i <= k; ++i ){
scanf("%d", &t);
must[t] = ;
} int a, b;
for( int i = ; i <= n-; ++i ){
scanf("%d%d", &a, &b);
G[a].push_back(b), G[b].push_back(a);
} solve(); return ;
}

当然,也可以不需要g数组,直接当一维背包来做

dp[root][x] = max( dp[root][x], dp[root][x-y] + dp[child_id][y] );

 #include<bits/stdc++.h>

 using namespace std;
const int maxn = + ;
int n, k, m; vector<int> G[maxn], G1[maxn];
int fa[maxn], w[maxn], must[maxn], vis[maxn];
int f[maxn][maxn]; void init(){
for( int i = ; i < maxn; ++i ){
G[i].clear();
G1[i].clear();
}
memset( fa, -, sizeof(fa) );
memset( f, -, sizeof(f) );
memset( must, , sizeof(must) );
} void dfs( int u, int pa ){
fa[u] = pa;
for( int i = ; i < G[u].size(); ++i ){
int v = G[u][i];
if( v == pa )
continue;
dfs( v, u );
}
} void dfs1( int u, int pa ){
if( pa != - ){
if( vis[pa] && !vis[u] )
G1[].push_back(u);
else if( !vis[pa] && !vis[u] )
G1[pa].push_back(u);
} for( int i = ; i < G[u].size(); ++i ){
int v = G[u][i];
if( v == pa )
continue;
dfs1( v, u );
}
} void dp(int root, int pa){
f[root][] = w[root];
for( int i = ; i < G1[root].size(); ++i ){
int v = G1[root][i];
if( v == pa )
continue;
dp( v, root );
for( int x = m; x >= ; --x ){
for( int y = ; y < x; ++y ){
f[root][x] = max( f[root][x], f[root][x-y] + f[v][y] );
}
}
}
} void solve(){
dfs(, -); //must½ÚµãѹËõ
int ans = , cnt = ;
memset( vis, , sizeof(vis) );
for( int i = ; i <= n; ++i ){
if(must[i]){
int u = i;
while(u != - && !vis[u]){
vis[u] = ;
cnt++, ans += w[u];
u = fa[u];
}
}
}
//cout << "ans: " << ans << endl; if( cnt > m ){
printf("-1\n");
return;
} //rebuild tree
dfs1( , - ); w[] = ans;
dp(, -);
printf("%d\n", f[][m-cnt+]);
} int main(){
//freopen("1.in", "r", stdin);
init();
scanf("%d%d%d", &n, &k, &m);
for( int i = ; i <= n; ++i ){
scanf("%d", &w[i]);
} int t;
for( int i = ; i <= k; ++i ){
scanf("%d", &t);
must[t] = ;
} int a, b;
for( int i = ; i <= n-; ++i ){
scanf("%d%d", &a, &b);
G[a].push_back(b), G[b].push_back(a);
} solve(); return ;
}

hiho一下第76周《Suzhou Adventure》的更多相关文章

  1. 圆内,求离圆心最远的整数点 hiho一下第111周 Farthest Point

    // 圆内,求离圆心最远的整数点 hiho一下第111周 Farthest Point // 思路:直接暴力绝对T // 先确定x范围,每个x范围内,离圆心最远的点一定是y轴两端的点.枚举x的范围,再 ...

  2. HihoCoder 1104 : Suzhou Adventure(树形DP)

    Suzhou Adventure 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Little Hi is taking an adventure in Suzhou n ...

  3. hiho一下 第115周:网络流一•Ford-Fulkerson算法 (Edmond-Karp,Dinic,SAP)

    来看一道最大流模板水题,借这道题来学习一下最大流的几个算法. 分别用Edmond-Karp,Dinic ,SAP来实现最大流算法. 从运行结过来看明显SAP+当前弧优化+gap优化速度最快.   hi ...

  4. 【hiho一下第77周】递归-减而治之 (MS面试题:Koch Snowflake)

    本题是一道微软面试题,看起来复杂,解出来会发现其实是一个很简单的递归问题,但是这道题的递归思路是很值得我们反复推敲的. 原题为hihocoder第77周的题目. 描述 Koch Snowflake i ...

  5. hiho一下 第207周

    题目1 : The Lastest Time 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 What is latest time you can make with ...

  6. hiho一下第128周 后缀自动机二·重复旋律5

    #1445 : 后缀自动机二·重复旋律5 时间限制:10000ms 单点时限:2000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...

  7. 【hiho一下】第一周 最长回文子串

    题目1:最长回文子串 题目原文:http://hihocoder.com/contest/hiho1/problem/1 [题目解读] 题目与 POJ 3974 palindrome 基本同样.求解最 ...

  8. Solution: 最近公共祖先·一 [hiho一下 第十三周]

    题目1 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在其中 ...

  9. hiho一下十六周 RMQ-ST算法

    RMQ-ST算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho在美国旅行了相当长的一段时间之后,终于准备要回国啦!而在回国之前,他们准备去超市采购一些当 ...

随机推荐

  1. Spring MVC 中的基于注解的 Controller(转载)

           终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...

  2. spring-boot启动自动执行sql文件失效 解决办法

    在springboot1.5及以前的版本,要执行sql文件只需在applicaion文件里指定sql文件的位置即可.但是到了springboot2.x版本, 如果只是这样做的话springboot不会 ...

  3. 【剑指Offer】64、滑动窗口的最大值

      题目描述:   给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{ ...

  4. C#第十五节课

    函数复习 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System. ...

  5. 洛谷P1060 开心的金明【dp】

    金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行&qu ...

  6. VirtualBox安装增强包实现文件共享

    环境: win10 64位 Virtualbox 5.1.30 ubuntu-16.04.3-server-amd64.iso 1. 安装好ubuntu后,打开virtualbox安装路径文件夹,找到 ...

  7. apache下部署showdoc

    1. 安装apache2服务器sudo apt-get install apache2 2. 安装php环境 sudo apt-get install php7.1 libapache2-mod-ph ...

  8. 2019-04-12 SQL 主键约束

    create table dbo.AssetPool( ID bigint not null, poolname nvarchar(50)not null, constraint pk_AssetPo ...

  9. 从0到1发布一个Vue Collapse组件

    需求背景 最近在项目中遇到了一个类似Collapse的交互需求,因此到github上找了一圈关于Vue Collapse的相关轮子,但是多少都有些问题.有的是实现问题,例如vue2-collapse, ...

  10. lucene_04_解析语法查询

    解析语法查询就是调用方法查询的原始查询 例如: 查询所有的查询器的语法为:*:*,因为lucene查询是根据term来做的,既是:key:value类型.*:*表示所有域中的所有值. api调用语法解 ...