吐槽:这道题真心坑...做了一整天,我太蒻了...

题意

构造一棵 $ n $ 个节点的树,要求满足以下条件:

  • 每个非叶子节点至少包含2个儿子;
  • 以节点 $ i $ 为根的子树中必须包含 $ c_i $ 个节点。

给出 $ n (n<=24)$ 和 $ c_i $ ,问是否存在符合条件的树。

分析

看到数据范围,第一时间想到的方法应该是状压DP,用数 $ s $ 表示已经被选入子树的点的集合,将两个以上小的子树合并为大的子树,看最后能否得到 $ s=1<<(n-1)-1 $ 这一集合。这么做的话,你就会…… 爆炸般地TLE

为什么?稍加分析就会发现,一共有 $ 2^n-1 $ 种状态,超过了 $ 1600 $ 万,其中绝大多数都是无法合并(有交集)的,遍历的时间大大超过了转移的时间。并且,题目问的是树是否存在,并不需要求出每一个状态,我们做了非常多的冗余操作。事实上,我们只需要找到一组解就可以了。那么,怎么快速确定某棵子树是否存在呢?

深搜+贪心。

我们发现,在确定比较大的子树时,我们总是需要用到小的子树,而大子树的存在性只与小子树的大小有关,与组合方式无关,所以我们可以dfs搜索子树的组合,并且一搜到解就退出。那么按照什么样的顺序搜索组合呢?可以直观地想到,在一开始放入的子树越大,越容易出解,所以我们在搜索每个子树的组合时按照从大到小的顺序搜,搜到解后删除原来的子树集合并用新的大子树代替以防止重复搜索,最后判断 $ 111...1 $ 这一集合是否存在。

另外,在读入 $ c_i $ 后按照大小排序,可以降低编程复杂度。

代码

AC,15ms,CF评测机真乃神机也

//by sclbgw7
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define R register
using namespace std;
int si[25];//保存子树大小
vector<int> dp[25];//dp[i]存放表示大小为i的子树的集合 int dfs(int s,int x,int y)
{
if(x==0)
{
if(!b[s])
dp[si[y]].push_back(s),b[s]=1;
return 1;
}
if(s)//如果已经选择了一些小子树
{
for(int i=x;i>=1;--i)
for(int j=dp[i].size()-1;j>=0;--j)
if(!(s&dp[i][j]))
if(dfs(s|dp[i][j],x-i,y))
{dp[i].pop_back();return 1;}
return 0;
}
s=(1<<(y-1));
for(int i=x-2;i>=1;--i)//内部节点必须有两个以上的儿子
for(int j=dp[i].size()-1;j>=0;--j)
if(!(s&dp[i][j]))
if(dfs(s|dp[i][j],x-i-1,y))
{dp[i].pop_back();return 1;}
return 0;
} int main()
{
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&si[i]);
sort(si+1,si+1+n);
int i;
for(i=1;si[i]==1;++i)
dp[1].push_back(1<<(i-1));
for(;i<=n;++i)
dfs(0,si[i],i);
if(dp[n].size())
printf("YES\n");
else
printf("NO\n");
return 0;
}

Codeforces 429C Guess the Tree(状压DP+贪心)的更多相关文章

  1. [多校联考2019(Round 5 T1)] [ATCoder3912]Xor Tree(状压dp)

    [多校联考2019(Round 5)] [ATCoder3912]Xor Tree(状压dp) 题面 给出一棵n个点的树,每条边有边权v,每次操作选中两个点,将这两个点之间的路径上的边权全部异或某个值 ...

  2. Codeforces Round #363 LRU(概率 状压DP)

    状压DP: 先不考虑数量k, dp[i]表示状态为i的概率,状态转移方程为dp[i | (1 << j)] += dp[i],最后考虑k, 状态表示中1的数量为k的表示可行解. #incl ...

  3. codeforces 8C. Looking for Order 状压dp

    题目链接 给n个物品的坐标, 和一个包裹的位置, 包裹不能移动. 每次最多可以拿两个物品, 然后将它们放到包里, 求将所有物品放到包里所需走的最小路程. 直接状压dp就好了. #include < ...

  4. Codeforces 895C Square Subsets(状压DP 或 异或线性基)

    题目链接  Square Subsets 这是白书原题啊 先考虑状压DP的做法 $2$到$70$总共$19$个质数,所以考虑状态压缩. 因为数据范围是$70$,那么我们统计出$2$到$70$的每个数的 ...

  5. 【题解】codeforces 8c Looking for Order 状压dp

    题目描述 Lena喜欢秩序井然的生活.一天,她要去上大学了.突然,她发现整个房间乱糟糟的--她的手提包里的物品都散落在了地上.她想把所有的物品都放回她的手提包.但是,这里有一点问题:她一次最多只能拿两 ...

  6. 刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)

    题目: lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输 ...

  7. Codeforces 895C Square Subsets:状压dp【组合数结论】

    题目链接:http://codeforces.com/problemset/problem/895/C 题意: 给你n个数a[i].(n <= 10^5, 1 <= a[i] <= ...

  8. CodeForces 599E Sandy and Nuts 状压DP

    题意: 有一棵\(n(1 \leq n \leq 13)\)个节点的树,节点的标号为\(1 \sim n\),它的根节点是\(1\). 现在已知它的\(m(0 \leq m < n)\)条边,和 ...

  9. 【9.7校内测试】【二分+spfa】【最长上升子序列】【状压DP+贪心(?)】

    刘汝佳蓝书上的题,标程做法是从终点倒着$spfa$,我是二分答案正着$spfa$判断可不可行.效果是一样的. [注意]多组数据建边一定要清零啊QAQ!!! #include<iostream&g ...

随机推荐

  1. python基础之函数进阶之函数作为返回值/装饰器

    因为装饰器需要用到返回函数的知识,所以在这里将返回函数和装饰器合并讲解. 什么是返回函数? 我们知道,一个函数中return可以返回一个或者多个值,但其实,return不仅可以返回值,还可以返回函数. ...

  2. 利用ImageOps调整图片的Aspect Ratio(给图片添加borders)

    # -*- coding: utf-8 -*- #******************** # 改变图片的纵横比(aspect retio) # 使用ImageOps.expand() # Image ...

  3. Spring boot 集成 mybaits plus(代码生成器)

    源码地址:https://github.com/YANGKANG01/Spring-Boot-Demo 代码生成操作 在pom.xml文件中引入以下包: <!-- mybatisplus与spr ...

  4. srpingboot2 session过期时间设置

    springboot2 设置session过期的配置 server.servlet.session.timeout = 1800 而不再是 server.session.timeout=1800

  5. yum安装_yum命令的相关操作

    2017年1月11日, 星期三 yum安装的四种方式 一.默认:从国外下载 二.国内:从阿里获取  http://mirrors.aliyun.com 1. cd /etc/yum.repos.d 2 ...

  6. python学习笔记3-函数的递归

    递归就是指自己函数的自我调用 #递归 #自己调用自己,函数的循环 def test1(): num = int(input('please enter a number:')) if num%2==0 ...

  7. 实验九 Web安全基础

  8. 20155217 2016-2017-2 《Java程序设计》第4周学习总结

    20155217 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 第六章 在java中,继承时使用extends关键字,private成员也会被继承,只不过子 ...

  9. 【leetcode 简单】 第九十五题 数字转换为十六进制数

    给定一个整数,编写一个算法将这个数转换为十六进制数.对于负整数,我们通常使用 补码运算 方法. 注意: 十六进制中所有字母(a-f)都必须是小写. 十六进制字符串中不能包含多余的前导零.如果要转化的数 ...

  10. Infinite Fraction Path(HDU6223 + bfs + 剪枝)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6223 题目: 题意: 给你一个长度为n的数字串,开始时你选择一个位置(记为i,下标从0开始)做为起点 ...