题目

题目大意

给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起。所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起)。
N ≤ 200 N \leq 200 N≤200


思考历程

看了题目好久,没有什么思路。
想到DP,但不知道怎么用DP做。
然后去翻翻题解,然后一脸懵逼,再去问问几位大佬。
LYL、ZHJ、GMH这三个大爷都说这题很水,是联赛难度。
不屑于给我讲。
天哪,这就是人与人之间的差距!太恐怖了。
然后我只能依靠我自己硬是刚了四天,对,是四天。
终于搞了出来……


正解

首先这题是一个树形DP。
看到这题的数据范围这么小,嗯,先估计一个时间复杂度。
就是 O ( N 3 ) O(N^3) O(N3)了
就这么随意
然后想想DP的状态
设 f i , j f_{i,j} fi,j​表示在 i i i这个节点的子树中(除 i i i以外),留下了 j j j个人的最优解。(**在这个时候,其它的人被丢到了 i i i处
所谓的 j j j个东西要和 i i i接在一起。
注意,对于 i i i这棵子树,有可能会有其它的节点进去里面,所以,这个 j j j可以大于 s u m s o n sum_{son} sumson​( s u m i sum_i sumi​表示 i i i这个子树中的人的数量)
然后考虑如何转移。
我们分类讨论:

  1. 如果没有人在 s o n son son这个节点上,那么 s o n son son这棵子树里面的人一定都会跑出来(不然就断了),所以留下的为空。那么此时 f x , i = f s o n , 0 + s u m s o n + f x , i f_{x,i}=f_{son,0}+sum_{son}+f_{x,i} fx,i​=fson,0​+sumson​+fx,i​
  2. 如果有人在 s o n son son这个节点上,设留在 s o n son son这棵子树内的人数为 j j j。显然 j ≠ 0 j \neq 0 j̸​=0
    注意一下我设的这个状态,你会发现这个状态不包括这个根节点。
    如果有人在 s o n son son这个节点上,那么子树剩余的人数为 j − 1 j-1 j−1。
    由于有 s u m s o n − j sum_{son}-j sumson​−j个人要出来,所以加上它们的贡献。注意,有可能是进去的,所以要用绝对值。
    f x , i = f s o n , j − 1 + ∣ s u m s o n − j ∣ + f x , i − j f_{x,i}=f_{son,j-1}+|sum_{son}-j|+f_{x,i-j} fx,i​=fson,j−1​+∣sumson​−j∣+fx,i−j​

DP搞完了,然后呢?每次换根重新操作?
当然不存在的,那样就有可能T飞了。
我们枚举每一个节点,将其作为最后形成的块的顶端。
在它子树内的东西都已经搞好了,所以,只需要搞一搞子树外的东西。
然后我们就可以发现只需要将它子树外的人到它距离之和加起来就好了。
因为这个点是整块的顶端,所以那些点必定会钻到下面去。
钻到下面之前肯定要先来到这个地方,剩下的就加上DP出来的结果就行了。
那么用什么来处理这个距离的和呢?
一开始,由于强迫症,我想用一些好的方法。
后来想想,反正范围这么小,那么简单粗暴一下就好了。
直接枚举子树外面的节点,然后一个一个地将距离加上(Floyed预处理)。
判断是否在子树内用 d f n dfn dfn序
时间复杂度 O ( N 3 ) O(N^3) O(N3)
是不是和预想的一模一样


其实这个题真的有点奇怪,的确不是很难,但是容易误入歧途。
在一开始,我设的状态为: f i , j f_{i,j} fi,j​表示在 i i i的子树内,留下 j j j个。
和最后的状态有什么区别呢?区别其实不大,就是包不包括 i i i这个节点。
然后我发现这样设繁琐至极,自己在推理的过程中绕来绕去的,很晕。
因为在这个东西中,我需要考虑一下根节点有没有人。
如果一开始没有人,那就要拿下面的补上,然后我又想着要开多一维来记录目前的这一块中的深度最小的点,用它来补上去,然后特别麻烦,怎么转移都不知道。
后来想想,我的DP可以设再设一下,表示根节点是否有东西……
我们知道在最后根节点一定有东西,或者所有的东西都跑到外面去,不可能出现根节点为空,而它的儿子的子树中还有东西。
那么在转移时还是有点麻烦。
最后,经过GMH的一点指导,然后我就又想了好久,才想了出来。
状态的这一点改动,实际上是解法的一个大大的更新。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define N 200
#define INF 400000
int n;
char a[N+3];
struct EDGE{int to;EDGE *las;
} e[N*2+1];
int ne;
EDGE *last[N+1];
inline void link(int u,int v){++ne;e[ne].to=v,e[ne].las=last[u];last[u]=e+ne;
}
int dfn[N+1],nowdfn;
int siz[N+1],sum[N+1];
int f[N+1][N+1];
void dp(int,int);
int dis[N+1][N+1];
void dfs(int,int);
int ans;
int main(){freopen("party.in","r",stdin);freopen("party.out","w",stdout);scanf("%d%s",&n,a+1);for (int i=1;i<n;++i){int u,v;scanf("%d%d",&u,&v);link(u,v),link(v,u);}for (int i=1;i<=n;++i)fill(f[i],f[i]+n+1,INF);dp(1,0);for (int i=1;i<=n;++i)fill(dis[i],dis[i]+n+1,INF);for (int i=1;i<=n;++i)dis[i][i]=0;for (int i=1;i<=n;++i)for (EDGE *ei=last[i];ei;ei=ei->las)dis[i][ei->to]=dis[ei->to][i]=1;for (int k=1;k<=n;++k)for (int i=1;i<=n;++i)for (int j=1;j<=n;++j)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);ans=INF;dfs(1,0);printf("%d\n",ans);return 0;
}
void dp(int x,int fa){dfn[x]=++nowdfn;siz[x]=1;if (a[x]=='0')sum[x]=0;elsesum[x]=1;f[x][0]=0;for (EDGE *ei=last[x];ei;ei=ei->las)if (ei->to!=fa){dp(ei->to,x);siz[x]+=siz[ei->to],sum[x]+=sum[ei->to];for (int i=siz[x]-1;i>=0;--i){//i倒着枚举,就像是背包问题一样。另外,由于目前的节点总数为siz[x],而x不算,所以最多为siz[x]-1//转移,具体理由见上f[x][i]=f[ei->to][0]+sum[ei->to]+f[x][i];for (int j=1;j<=i;++j)f[x][i]=min(f[x][i],f[ei->to][j-1]+abs(sum[ei->to]-j)+f[x][i-j]);}}
}
void dfs(int x,int fa){int s=0;for (int i=1;i<=n;++i)if (a[i]=='1' && !(dfn[x]<=dfn[i] && dfn[i]<dfn[x]+siz[x]))//后面的这个东西判断它是否在子树里面s+=dis[i][x];ans=min(ans,s+f[x][sum[1]-1]);//因为总共的人数是sum[1],而有一个在x上,所以为sum[1]-1for (EDGE *ei=last[x];ei;ei=ei->las)if (ei->to!=fa)dfs(ei->to,x);
}

总结

首先,在膜拜一下三个大佬。
还有后来随随便便AC的ZSH、PZM大佬。
对于在一棵树上又看起来不好用数据结构什么的东西来做的题目的时候,就想想能不能树形DP。
对于这题,树形DP的最大的妙处在于根节点不包括在内。
根节点有时候会处于一种比较尴尬的状况,所以,我们就要对它特殊地对待。
然后在状态中就把根节点排除在外,暂时不考虑。

其它的事

我翻了翻几位大佬的代码,都是枚举根节点然后搞一遍DP。
你们怎么不会TLE?而且,为什么,为什么时间还比我短!!!
有种被欺骗了的感觉。
还有这题的数据是真的水。
我的代码之前有一个很严重的错误,然而,带着这个错误,我居然有90分!
So strange!
这个错误是,Floyed中初值没有赋值好……
所以我不保证我现在这个代码一定是对的,但这个思想还是值得借鉴。

JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)相关推荐

  1. jzoj 5970.【北大2019冬令营模拟12.1】space 莫比乌斯反演

    Description Input Output Sample Input 2 1 2 2 1 1 2 2 1 Sample Output 24 样例解释: Data Constraint 分析: 对 ...

  2. JZOJ 5977. 【清华2019冬令营模拟12.15】堆

    Description Input Output Sample Input 10 10 0 1 1 2 2 4 3 12 2 6 2 15 3 5 3 10 7 7 9 16 2 3 1 10 9 2 ...

  3. JZOJ 5984. 【北大2019冬令营模拟2019.1.1】仙人掌

    Description Input Output Sample Input 5 10 1 2 2 3 2 4 3 5 1 5 2 4 3 5 4 2 3 1 Sample Output 2060 Da ...

  4. jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)

    题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...

  5. jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)

    这其实是道打表题--你看我代码就知道了-- 咳咳来点严谨证明好了-- 前方高能请注意 首先,正多边形近似于圆,可以看做在圆里内接多边形.圆内接多边形最多只有三个锐角.因为凸多边形的外角和为\(360\ ...

  6. jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)

    题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...

  7. jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)

    题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...

  8. 【清华2019冬令营模拟12.8】视野

    计算几何弱渣果然就是一点感觉也没有. 题目大意: 题解: 首先考虑不删怎么做? 肯定要把点给离散,那么现在对于每一小段,要求出是哪条线段最近? 按一个顺序扫过去,每一条线段打一个加入和删除的标记. 由 ...

  9. 2018冬令营模拟测试赛(十八)

    2018冬令营模拟测试赛(十八) [Problem A]Table 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见"试题描述&qu ...

最新文章

  1. 微信小程序教学第二章(含视频):小程序中级实战教程之预备篇 - 封装网络请求及 mock 数据...
  2. ubi-partman failed with exit code 141
  3. 远程连接计算机用户名怎样修改,更改服务器远程桌面用户名
  4. python读取文件第n行-Python读取文件最后n行的方法
  5. vim如何开启256色
  6. java query object_java queryforobject
  7. 数据分析--商业报告实例
  8. 1、微博RPC框架Motan
  9. NEC电话交换机基础操作
  10. 贝叶斯回归 matlab,逻辑回归模型的贝叶斯分析
  11. 光功率 博科交换机_交换机是否支持查看光模块型号及收发光功率
  12. android 画布实现签名,Android实现屏幕手写签名
  13. C/C++探秘(1)
  14. 报表工具使用教程-FineReport决策报表导出Plus
  15. 解决mysql保存数据SQLException: Incorrect string value: ‘\xF0\x9F\x91\x87\xE5\x91...‘ for column ‘错误
  16. vue设置scrollTop不起作用
  17. [转]触乐独家:揭秘愈演愈烈的苹果“做号退款”生态圈
  18. 当代年轻人下班行为报告:我下班了,却又没完全下班
  19. Java API文档的使用详解
  20. 马云说的到底对不对,京东到底行不行?

热门文章

  1. [4G/5G/6G专题基础-160]: BLER与MCS的关系、MCS表格的选择
  2. js如何实现随机数切换
  3. [Swift] 数组恒等 === 的 bug?!
  4. Win10蓝牙突然消失的解决办法
  5. 王者荣耀小游戏(发给你的好兄弟(大怨种)^-^)
  6. win7 屏保播放视频
  7. 基于单片机(STC89C52)的数字频率计
  8. 使用TurboMail邮件系统的智能公文模块
  9. word简单三线表制作(分列、分行)
  10. 16 Three.js使用dat.GUI简化试验流程