【树形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:经过思考,我对这题有了更深的理解,现将更新内容放在原题解下方. ...
随机推荐
- apache mod_alias模块功能介绍
我觉得mod_alias根mod_rewrite挺像的,都可以实现url的重写,而mod_alias可以实现简单的url重写的功能 ,而mod_rewrite可以实现比较复杂的重写.mod_alias ...
- html/css实现聊天布局
效果图 项目结构 html代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" / ...
- AJPFX浅谈关于Java程序员缺乏面向对象的基本功的问题
为什么很多 Java 程序员会缺乏面向对象基本功?这得怪那些 Java 框架.现在 Java 的各种框架太发达.太傻瓜化了,导致很多程序员只需要按部就班.照着框架进行代码填空,基本已经丧失了 OOA ...
- This is such a crock of shit—From Scent of a woman
- Mr. Slade. - This is such a crock of shit! - Mr. Trask. - Please watch your language, Mr. Slade. Y ...
- checking for gcc... no
./configure 后显示checking for gcc... nochecking for cc... nochecking for cl.exe... noconfigure.sh:erro ...
- .net4.5注册到iis
开始->所有程序->附件->鼠标右键点击“命令提示符”->以管理员身份运行->%windir%\Microsoft.NET\Framework\v4.0.30319\as ...
- Android手机app耗电量测试工具 - Gsam Battery Monitor
这段时间需要测试一个Android手机app的耗电量,在网上找了一个工具,Gsam Battery Monitor,觉得挺好用,和大家分享一下. 安装app后打开,可以看到主界面是这样的 点击一下上图 ...
- vue下axios和fetch跨域请求
1.在config的index.js下面进行常用跨域配置代码:proxyTable: { '/apis': { //使用"/api"来代替"http://xxxx.cn& ...
- log4j 日志分级处理
log4j 配置文件: log4j.rootLogger=debug,stdout,debug,info,errorlog4j.appender.stdout=org.apache.log4j.Con ...
- HDU 5381 The sum of gcd (技巧,莫队算法)
题意:有一个含n个元素的序列,接下来有q个询问区间,对每个询问区间输出其 f(L,R) 值. 思路: 天真单纯地以为是道超级水题,不管多少个询问,计算量顶多就是O(n2) ,就是暴力穷举每个区间,再直 ...