【树形DP】codeforces K. Send the Fool Further! (medium)
http://codeforces.com/contest/802/problem/K
【题意】
给定一棵树,Heidi从根结点0出发沿着边走,每个结点最多经过k次,求这棵树的最大花费是多少(同一条边走n次花费只算一次)
【思路】
对于结点v:
- 如果在v的某棵子树停下,那么可以“遍历”k棵子树(有的话)
- 如果还要沿着v返回v的父节点p,那么只能“遍历”k-1棵子树(有的话)。
用dp[v][1]表示第一种情况,dp[v][0]表示第二种情况;最后要求的就是dp[0][0]。
1. 对于dp[v][1],把所有的子树从大到小排序
(t=k-1)
2. 对于dp[v][0],枚举子结点dp[u][0]中的u,剩下的k-1个dp[u][1]取最大的,所以我们可以这样预处理:
sum=
(t=k)
- 如果u<k,则target=sum-dp[u][1]+dp[u][0]
- 否则, target=sum-dp[t][1]+dp[u][0](t是从大到小排序后的第k-1个)
这样,dp[0][0]就是所求结果(dp[0][0]一定大于dp[0][1]),时间复杂度是O(nlogn)
【官方题解】
【Accepted】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm> using namespace std;
int n,m;
vector< vector< pair<int,int> > > g;
const int maxn=1e5+;
int dp[maxn][];
void dfs(int v,int p,int edge)
{
//从p到v的花费要算在v里
dp[v][]+=edge;
dp[v][]+=edge;
vector< pair<int,int> > s;
//只有根结点没有父节点,非根结点有父节点,减去1
if(v==)
{
s.resize(g[v].size());
}
else
{
s.resize(g[v].size()-);
}
//遍历
int num=;
for(int i=;i<g[v].size();i++)
{
int to=g[v][i].first;
if(to==p)
{
continue;
}
dfs(to,v,g[v][i].second);
s[num++]={dp[to][],to};
}
//从大到小排序
sort(s.begin(),s.end());
reverse(s.begin(),s.end());
//要记录各个子结点的rank,后面dp[v][0]枚举u是要分类
int pos[maxn];
for(int i=;i<s.size();i++)
{
pos[s[i].second]=i;
}
//计算dp[v][1]
for(int i=;i<min(m-,(int)s.size());i++)
{
dp[v][]+=s[i].first;
}
//计算dp[v][0]
int sum=;
for(int i=;i<min(m,(int)s.size());i++)
{
sum+=s[i].first;
}
int maxu=-;
//枚举
for(int i=;i<g[v].size();i++)
{
int to=g[v][i].first;
if(to==p)
{
continue;
}
if(pos[to]<m)
{
maxu=max(maxu,sum-dp[to][]+dp[to][]);
}
else
{
maxu=max(maxu,sum-s[m-].first+dp[to][]);
}
}
if(maxu>-)
{
dp[v][]+=maxu;
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(dp,,sizeof(dp));
g.resize(n);
int u,v,c;
for(int i=;i<n-;i++)
{
scanf("%d%d%d",&u,&v,&c);
g[u].push_back({v,c});
g[v].push_back({u,c});
}
//根结点为0,无父结点,根结点到父结点的花费也为0
dfs(,,);
printf("%d\n",dp[][]);
}
return ;
}
注意vector开始要resize.....orz
【WA】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath> using namespace std;
int n,k;
const int maxn=2e5+;
struct edge
{
int to;
int nxt;
int c;
}e[maxn];
int head[maxn];
int tot;
struct node
{
int x;
int id;
}sz[maxn];
int rk[maxn];
bool cmp(node a,node b)
{
return a.x>b.x;
}
void init()
{
memset(head,-,sizeof(head));
tot=;
} void add(int u,int v,int c)
{
e[tot].to=v;
e[tot].c=c;
e[tot].nxt=head[u];
head[u]=tot++;
}
int dp[maxn][]; int dfs(int u,int pa,int c)
{
dp[u][]=c;
dp[u][]=c;
int cnt=;
for(int i=head[u];i!=-;i=e[i].nxt)
{
int v=e[i].to;
int c=e[i].c;
if(v==pa) continue;
dfs(v,u,c);
sz[cnt].x=dp[v][];
sz[cnt++].id=v;
}
sort(sz,sz+cnt,cmp);
for(int i=;i<min(cnt,k-);i++)
{
dp[u][]+=sz[i].x;
}
int sum=;
for(int i=;i<min(cnt,k);i++)
{
sum+=sz[i].x;
}
int ans=;
for(int i=;i<cnt;i++)
{
if(i<k)
{
ans=max(ans,sum-sz[i].x+dp[sz[i].id][]);
}
else
{
ans=max(ans,sum-sz[k-].x+dp[sz[i].id][]);
}
}
dp[u][]+=ans;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
init();
memset(dp,,sizeof(dp));
for(int i=;i<n-;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
dfs(,-,);
cout<<dp[][]<<endl;
}
return ;
}
Wrong Answer
终于弄清楚了这个为什么WA!因为我在dfs里用了一个全局变量sz来保存{dp[v][1],v}。然而这是一个全局变量,所以一层里的正确值会被另一层修改!比如当我递归到0时已经有了正确值sz[0].w=5,sz[0].v=2;然而再递归到0的另一分枝1的时候,会修改sz[0],最后再回溯到0时sz[0]已经不是当年的sz[0]了!
所以还是用vector临时申请吧!
【AC(一个更优美的代码)】
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath> using namespace std;
int n,k;
const int maxn=2e5+;
struct edge
{
int to;
int nxt;
int c;
}e[maxn];
int head[maxn];
int tot;
int dp[maxn][]; struct node
{
int x;
int id;
node(){}
node(int _x,int _id):x(_x),id(_id){}
bool operator<(const node & nd) const
{
return x>nd.x;
}
}; void init()
{
memset(head,-,sizeof(head));
tot=;
} void add(int u,int v,int c)
{
e[tot].to=v;
e[tot].c=c;
e[tot].nxt=head[u];
head[u]=tot++;
} int dfs(int u,int pa,int c)
{
dp[u][]=c;
dp[u][]=c;
vector<node> s;
for(int i=head[u];i!=-;i=e[i].nxt)
{
int v=e[i].to;
int c=e[i].c;
if(v==pa) continue;
dfs(v,u,c);
s.push_back(node(dp[v][],v));
}
sort(s.begin(),s.end());
int sz=s.size();
for(int i=;i<min(sz,k-);i++)
{
dp[u][]+=s[i].x;
}
int sum=;
for(int i=;i<min(sz,k);i++)
{
sum+=s[i].x;
}
int ans=;
for(int i=;i<sz;i++)
{
if(i<k)
{
ans=max(ans,sum-s[i].x+dp[s[i].id][]);
}
else
{
ans=max(ans,sum-s[k-].x+dp[s[i].id][]);
}
}
dp[u][]+=ans;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
init();
memset(dp,,sizeof(dp));
for(int i=;i<n-;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
dfs(,-,);
cout<<dp[][]<<endl;
}
return ;
}
如果是vector<pair<int,int>> 要从大到小排序,可以先sort(s.begin(),s.end()),再reverse(s.begin(),s.end())
【树形DP】codeforces K. Send the Fool Further! (medium)的更多相关文章
- 树形DP ---- Codeforces Global Round 2 F. Niyaz and Small Degrees引发的一场血案
Aspirations:没有结果,没有成绩,acm是否有意义?它最大的意义就是让我培养快速理解和应用一个个未知知识点的能力. ————————————————————————————————————— ...
- 树形dp - Codeforces Round #322 (Div. 2) F Zublicanes and Mumocrates
Zublicanes and Mumocrates Problem's Link Mean: 给定一个无向图,需要把这个图分成两部分,使得两部分中边数为1的结点数量相等,最少需要去掉多少条边. ana ...
- 树形DP Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland
题目传送门 /* 题意:求一个点为根节点,使得到其他所有点的距离最短,是有向边,反向的距离+1 树形DP:首先假设1为根节点,自下而上计算dp[1](根节点到其他点的距离),然后再从1开始,自上而下计 ...
- 树形dp Codeforces Round #364 (Div. 1)B
http://codeforces.com/problemset/problem/700/B 题目大意:给你一棵树,给你k个树上的点对.找到k/2个点对,使它在树上的距离最远.问,最大距离是多少? 思 ...
- Codeforces 802L Send the Fool Further! (hard)
Description 题面 题目大意:求从根节点出发,每次随机走一个相邻的点,问走到任意一个叶子节点经过的路径长度的期望(走到就停止) Solution 树上高斯消元,复杂度是 \(O(n)\) 的 ...
- VK Cup 2012 Round 1 D. Distance in Tree (树形dp)
题目:http://codeforces.com/problemset/problem/161/D 题意:给你一棵树,问你两点之间的距离正好等于k的有多少个 思路:这个题目的内存限制首先大一倍,他有5 ...
- codeforces 161D Distance in Tree 树形dp
题目链接: http://codeforces.com/contest/161/problem/D D. Distance in Tree time limit per test 3 secondsm ...
- Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)
题目:http://codeforces.com/contest/1153/problem/D 题意:给你一棵树,每个节点有一个操作,0代表取子节点中最小的那个值,1代表取子节点中最大的值,叶子节点的 ...
- Codeforces 1097G Vladislav and a Great Legend [树形DP,斯特林数]
洛谷 Codeforces 这题真是妙的很. 通过看题解,终于知道了\(\sum_n f(n)^k\)这种东西怎么算. update:经过思考,我对这题有了更深的理解,现将更新内容放在原题解下方. ...
随机推荐
- 【转】在 26 岁时写给 18 岁的自己--Livid
原文:http://livid.v2ex.com/essays/2012/01/24/a-letter-from-26-to-18/ 我知道现在的自己是再也回不去的了.可是倘若有机会,我是多么希望能让 ...
- qconbeijing2015
http://2015.qconbeijing.com/schedule 大会日程 2015年4月23日,星期四 地点 2号厅 203AB 201AB 9:15 开场致辞 专题 主题演讲 互联网金融背 ...
- 动手实现 React-redux(五):Provider
我们要把 context 相关的代码从所有业务组件中清除出去,现在的代码里面还有一个地方是被污染的.那就是 src/index.js 里面的 Index: ... class Index extend ...
- 常用的HTML5 pattern属性
type="tel" 和 type="number" 的区别 这里还是先那么先交代一下最初遇到的问题.其实无论是tel还是number都不是完美的: type= ...
- AJPFX辨析Java中运算符 ++ 和 += 的区别
我们都知道Java中 ++ 和 +=1 都是把数字增加一后,把值赋给左边,那二者有什么区别呢? i+=1 运行的底层Heap申请一个区域存放i,在数据区域开劈一个区域存放1,2个内存段被数据被送入到 ...
- UISegmentedControl去掉背景色与UIScrollView联动
UISegmentControl分段控制器是UIKit框架提供的一组按钮栏,提供多个可选的按钮,只能激活其中的一个,响应事件.主要用来在同一层次重要性下不同的信息展示或者不同的界面展示之间切换.例如手 ...
- 第3章 接口与API设计 52条笔记
第3章 接口与API设计 52条笔记 第15条: 用前缀避免命名空间冲突 Objective-C 没有其他语言那种内置的命名空间机制 .鉴于此,我们在起名时要设法避免潜在的命名冲突,否则很容易就重名了 ...
- 关于bin和obj文件夹。debug 和release的区别(转)
关于bin和obj文件夹. 楼主hcaihao(影子男孩)2002-05-29 20:04:24 在 .NET技术 / C# 提问 VS.Net会生成bin和obj文件夹以及它们下面的Debug和Re ...
- vue利用计算属性做(展开收起)小例子
<template> <div class="wrap"> <div class="box"> <div v-for= ...
- [Python學習筆記] 利用 Python在Excel 插入註解
用Python 來處理excel 檔 用過了 openpyxl 還有 pyexcel目前覺得除了讀寫如果還要使用另外的功能 (像是讀取格子裡的公式)可以用 xlwings 他的首頁標題 " ...