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)的更多相关文章

  1. 树形DP ---- Codeforces Global Round 2 F. Niyaz and Small Degrees引发的一场血案

    Aspirations:没有结果,没有成绩,acm是否有意义?它最大的意义就是让我培养快速理解和应用一个个未知知识点的能力. ————————————————————————————————————— ...

  2. 树形dp - Codeforces Round #322 (Div. 2) F Zublicanes and Mumocrates

    Zublicanes and Mumocrates Problem's Link Mean: 给定一个无向图,需要把这个图分成两部分,使得两部分中边数为1的结点数量相等,最少需要去掉多少条边. ana ...

  3. 树形DP Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland

    题目传送门 /* 题意:求一个点为根节点,使得到其他所有点的距离最短,是有向边,反向的距离+1 树形DP:首先假设1为根节点,自下而上计算dp[1](根节点到其他点的距离),然后再从1开始,自上而下计 ...

  4. 树形dp Codeforces Round #364 (Div. 1)B

    http://codeforces.com/problemset/problem/700/B 题目大意:给你一棵树,给你k个树上的点对.找到k/2个点对,使它在树上的距离最远.问,最大距离是多少? 思 ...

  5. Codeforces 802L Send the Fool Further! (hard)

    Description 题面 题目大意:求从根节点出发,每次随机走一个相邻的点,问走到任意一个叶子节点经过的路径长度的期望(走到就停止) Solution 树上高斯消元,复杂度是 \(O(n)\) 的 ...

  6. VK Cup 2012 Round 1 D. Distance in Tree (树形dp)

    题目:http://codeforces.com/problemset/problem/161/D 题意:给你一棵树,问你两点之间的距离正好等于k的有多少个 思路:这个题目的内存限制首先大一倍,他有5 ...

  7. codeforces 161D Distance in Tree 树形dp

    题目链接: http://codeforces.com/contest/161/problem/D D. Distance in Tree time limit per test 3 secondsm ...

  8. Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)

    题目:http://codeforces.com/contest/1153/problem/D 题意:给你一棵树,每个节点有一个操作,0代表取子节点中最小的那个值,1代表取子节点中最大的值,叶子节点的 ...

  9. Codeforces 1097G Vladislav and a Great Legend [树形DP,斯特林数]

    洛谷 Codeforces 这题真是妙的很. 通过看题解,终于知道了\(\sum_n f(n)^k​\)这种东西怎么算. update:经过思考,我对这题有了更深的理解,现将更新内容放在原题解下方. ...

随机推荐

  1. maven-ali镜像网站setting.xml

    简述 使用maven管理jar包的时候,有可能会有网络限制,要解决的这个问题的话可以使用ali的镜像网站. 创建文件 创建settings.xml,内容如下 <settings xmlns=&q ...

  2. JDK使用最多的模式之一--观察者模式

    公司接到新任务,需要做一个气象监测应用.该应用将实现三个界面:当前气象状态,气象统计以及气象预报.应用从WeatherObject对象中获取所需数据:温度,湿度,气压.当然,为了可扩展性,该应用同时也 ...

  3. hihocoder1718 最长一次上升子序列

    思路: 对于每个i,分别求1~i和i+1~N两部分的最长下降子序列“拼”起来,最终取最大长度即可.学习了如何使用BIT把LIS问题O(N2)算法优化为O(Nlog(N))的算法. https://ww ...

  4. javaee 第五周作业

    一.Ajax技术 AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). AJAX 不是新的编程语言,而是一种使用现有标准的新方法. ...

  5. Android(java)学习笔记166:上下文的区分

    1.两种上下文:  (1)Activity.this                               界面的上下文 (2)getApplicationContext()         整 ...

  6. how to get many stars on Github?

    some key points: 1: make a beautiful README file2: use some GIF (google some tools to convert videos ...

  7. Python3简明简称(八)—— 函数

    我们经常需要在同一个程序里多次复用代码.函数可以很好的帮助我们完成这一点.我们在函数里写我们要重复做的事,然后我们在任何需要的时候调用它.我们已经看到一些内建的函数,比如 len(),divmod() ...

  8. 如何改android device monitor文件的权限

    adb.exe在c/Android/platform-tools目录下,在这个目录下打开终端,然后adb shell,然后su http://blog.csdn.net/u012719153/arti ...

  9. 第1节 flume:6、flume的入门测试案例

    案例:使用网络telent命令向一台机器发送一些网络数据,然后通过flume采集网络端口数据. 1.2.1 Flume的安装部署 第一步:下载解压修改配置文件 Flume的安装非常简单,只需要解压即可 ...

  10. Oracle中的DDL,DML,DCL总结

    转自http://blog.csdn.net/w183705952/article/details/7354974 DML(Data Manipulation Language,数据操作语言):用于检 ...