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

算是第一次做树形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. Unity引擎GUI之Text

    Text 文本 要显示的字符串. Font 字体 Font Style 加粗与倾斜 Font Size 字体大小 Line Spacing 行距,文本行之间的间距 Rich Text 勾选后,想要单独 ...

  2. 协议(Protocol) 和代理(Delegate)

    1.概念与组成 delegate是iOS中一种常见的设计模式,是一种消息传递的的方式,常见的消息传递方式还有以下几种: 通知:在iOS中由通知中心进行消息接收和消息广播,是一种一对多的消息传递方式. ...

  3. [ller必读] LoveLive! 必备技能之 Python Pillow 自动处理截图

    起因 喜欢的歌,静静地听:喜欢的人,远远的看.30天前,就是3月14号,我情不自禁地走近了<LoveLive!学院偶像祭>,这是我的第一张卡片(见下图).第二天也就是3月15日,海未生日了 ...

  4. VS2013(Win10X64)-配置编译Caffe

    主要看这篇文章,有点小瑕疵,瑕不掩瑜.参考链接:http://www.bubuko.com/infodetail-902302.html 文中红色标记为文章小瑕疵的地方,在此文中标记出来,做为修改对上 ...

  5. C#抽奖算法

    摘自网络 static void Main(string[] args) { //各物品的概率保存在数组里 ]{ 0.5f, 0.5f, , }; //单次测试 //Console.WriteLine ...

  6. 【转载】使用 IntelliJ IDEA 新建一个 web项目

    IntelliJ IDEA 创建Web项目(全教程)   说明:IntelliJ IDEA 版本为14.JDK 版本为1.7tomcat 版本为apache-tomcat-7.0.70 注:在创建过程 ...

  7. 洛谷P1339 [USACO09OCT]热浪Heat Wave

    思路:裸SPFA过一遍(建议使用邻接链表存储),无向图,无向图,无向图,重要的事情要说三遍!!!蜜汁RE是什么鬼????第九个点数组开到20K,第十个点数组开到30K才AC.或许我代码写的有bug?( ...

  8. 把 Python 脚本打包成可以直接双击运行的 .exe 文件 【转】

    因为最近要用到 Python 脚本,所以自己学习了一下,顺便学习如何把它打包成 .exe 可执行文件,达到双击运行的效果,网上找了资料,保存下来学习用,原文出处:https://baijiahao.b ...

  9. redhat超级用户密码破解

    1. 开机在出现grub画面,按e键 2. 用上下键选中第二项(类似于kernel /boot/vmlinuz-2.4.18-14 ro root=LABEL=/) 然后按e键编辑 3. 空格sing ...

  10. 洛谷—— P1784 数独

    https://www.luogu.org/problem/show?pid=1784 题目描述 数独是根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个粗线宫内的数字 ...