NOIP2017 day2 t2 treasure题解

8 篇文章 0 订阅
2 篇文章 0 订阅

这是我第一次发文章,挺激动的。我未来所有的文章,都会保证是原创。

来看题目大意。给出n个点(n≤12,第一印象我想应该是可以使用暴力求解吧),m条边(m貌似有点大诶,远大于n²,这说明肯定有重边,开一个邻接矩阵就可以解决)组成的一个无向图。现在要生成一颗树,root节点的深度给0。树有一个总分,公式是:∑(i到他父节点的边权×i的深度)。请输出所有可能的生成树中最大的总分。

数据约定与分析:

(一)20分的数据,给定的图就是树

这20分稳稳的可以拿到。枚举以每个点为root,去做dfs。搞定√

(二)20分的数据,所有边权都相等(以下可能会是错误的,骗骗比较low的数据还是可以的)

不难看出,题目给定的公式是一个类似二元函数的式子。现在所有边权相等,相当于直接简化为一元函数——让每个点深度都尽量小。这不就是贪心吗?但是贪心也是有一些策略的。照样枚举以每个点作root,然后dfs,不同的是,加点要尽量压缩路径,能连上的就都要连上。那我们下一步对谁加点呢?

对于每一个现在在树中的点,我们枚举,找到一个所发出边(这个边必须要通向未加入树中的点,否则不计在其中)最多的点,用它去dfs。(这是第一关键字,要注意第二关键字是每个节点的深度)。

(三)prim(这个必然是错的)

考场上我的一个好朋友写了一个prim,其实我想过用最小生成树去解这道题目。但是写的过程中会发现,虽然连通性可以很轻松解决,但是如果遇到加的点不在树中,是不是就错了呢?(深度会乱)

(四)暴力(70分的解法)

加边既然不可以,那我们枚举1~n的所有全排列(next_permutation函数还是很赞的),记序列为p。以p[1]为root,然后不停的按顺序加点。对于一个p[i]枚举他前面的所有点(即p[1]~p[i-1]),贪心的去找一个((depth[j]+1)*两节点边权)中最小值对应的j,并把i与j连上。如果枚举时存在两个相等的,当然是保存深度最小的呀~

当然有一些序列好像很废,因为做到某节点p[i]后,你会发现并不能在前面的节点中找到与i相连的。这种情况就直接不要管他,一连串break和continue,跳过这个序列就好啦~

我写的代码:paste.ubuntu.com/26072084

(五)正解

n非常小,所以可以考虑做一个状压dp呀。f[i][s]表示,当前入树的节点的01状态表示为s,已经做到了第i层。这里是有言外之意的:再加进来的点强制放入第i+1层,不管你真实情况下放在哪。这时候你可能会问:这么dp不会导致运行的最终结果大于等于正确答案吗?我想,稍后再解释这个问题会更加清晰。

我们的所求是min(f[i][(1<<n)-1])。边界赋值:f[0][1<<(x-1)] = 0。其他都赋一个正无穷好了。

那么对于一个已有的f[i][s],我们寻找s的二进制补数记为s'。枚举s'的所有子集(即,s’中是1的位置可以换成0),假设我用k代表。那么我强制找到,每一个准备入树的点,到树中已有节点的最小权值(已有节点,指s中的所有是1的位置,并不可以像我们做暴力时采用的迭代加点思想!!!如果有任意一个点根本是连不进来的,那么很简单,这个状态废,下一个),给这些最小权值求和,直接乘以i+1,判断能否更改我的f[i+1][s|k],能就更新。

现在我来解释一下刚才的问题。我在枚举时枚举尽了所有可能的情况,如果现在存在一个解:入树元素状态为s,且同样做到了第i层的一种更优方案,那不需要考虑呀,我们早就在之前就已经处理到了。也就是说,大于我们真实情况的答案并不能影响到任何结果。

用这样的思路我们的复杂度是O(n³·3的n次方)。n³是因为:枚举深度、枚举子集、枚举与s的边。

我的代码:paste.ubuntu.com/26062656

(六)优化掉一个n

我们可以提前预处理出每个节点与每一个状态(虽然话是这么说,但是二者依然是不能有交集的)的最小边权值,这样就免得每次都去枚举了。

(七)再优化一个吧

这个我还没有想到。leoly学长跟我说让我简化枚举子集,大家要是想到什么思路可以给我留言。我想到之后也会第一时间发上来。


其实方法(五)就足以AC了~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值