题面:

传送门:https://www.luogu.org/problemnew/show/P2831


Solution

首先,我们可以先康一康题目的数据范围:n<=18,应该是状压或者是搜索。

事实上,这题搜索和状压DP都是能做的。

(因为搜索在我心中留下了阴影(斗地主),所以在这里,我讲状压DP的做法)

根据我们以往设计状压DP的经验,我们可以很轻松地设计这一题的状态:

设f[i]表示打下的猪猪的状态为i的方案数,(状态在这里用二进制方式来表示,例如:00101表示打下了第1和第3只猪)

那么有: f[i] = min(f[j])+1 (j为i的子集)

这里用到一个枚举子集的技巧,对于一个状态i,它可以这样枚举子集:

for(int j=i;j>=0;j=(j-1)&i) (至于证明,你可以在草稿纸上画画,很快就会发现它的精妙了)

那我们怎么判断能否从状态 j 转移到 i 呢?

首先,根据数学常识,我们需要3个x不一样的点才能确定一条抛物线。这题已经固定了原点了,所以我们还需要两个点来确定一条抛物线

如果j与i只有一个或两个x不同的点 是不同的,那显然是可以转移的。

对于有两个以上的点,我们可以用前两个点通过解二元一次方程来计算函数的a与b,然后再去一个一个判断每个不同的点是否在这条抛物线上。

对于如何解二元一次方程..........(这应该是数学常识吧)

复杂度O (3^n*n*T) 

显然TLE,事实上,这样做只能得60分。

那怎么优化复杂度呢?

刚刚的枚举子集显然是不可行了,那我们可以换个思路。

我们可以枚举点。

对于某一种状态,我们肯定可以枚举两个(或一个)没有用过的点去构成新的抛物线从而更新其他的状态。

这样子,我们成功地把复杂度降为了 O(2^n*n^2*T)

依然过不了,事实上,这样做能得85分。

上一个作法已经和正解很接近了。

我们可以考虑这样优化方程:

这样子,我们复杂度就降为了O(2^n*n*T)

就酱,我们就可以把这道题切掉啦(´▽`)ノ


Code

//Luogu P2831 愤怒的小鸟
//Sep,19th,2018
//状压DP
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=18+2;
const double eps=1e-7;
struct node
{
double x,y;
}nd[N];
long long f[1<<N];
int n,POW[N],g[N][N];
inline double pf(double x)
{
return x*x;
}
bool solve(node A,node B,double &a,double &b)
{
if(fabs(A.x-B.x)<=eps) return false;
a=(B.x*A.y-A.x*B.y)/(pf(A.x)*B.x-pf(B.x)*A.x);
b=(pf(B.x)*A.y-pf(A.x)*B.y)/(pf(B.x)*A.x-pf(A.x)*B.x);
if(a>=0) return false;
return true;
}
double fun(double x,double a,double b)
{
return a*pf(x)+b*x;
}
int main()
{
POW[0]=1;
for(int i=1;i<N;i++)
POW[i]=POW[i-1]*2;
int T,tt;
scanf("%d",&T);
for(;T>0;T--)
{
memset(g,0,sizeof g);
scanf("%d%d",&n,&tt);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&nd[i].x,&nd[i].y); for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double a=0,b=0;
bool OK=solve(nd[i],nd[j],a,b);
if(OK==false) continue;
for(int k=1;k<=n;k++)
if(fabs(fun(nd[k].x,a,b)-nd[k].y)<=eps)
g[i][j]+=POW[k-1];
} memset(f,0x3f,sizeof f);
f[0]=0;
int to=(1<<n)-1,used[N];
for(int i=0;i<to;i++)
{
memset(used,0,sizeof used);
int temp=i,j;
for(j=n-1;j>=0;j--)
if(temp-POW[j]>=0)
{
temp-=POW[j];
used[j+1]=true;
}
for(j=1;j<=n;j++)
if(used[j]==false)
break;
f[i|POW[j-1]]=min(f[i|POW[j-1]],f[i]+1);
for(int k=j+1;k<=n;k++)
if(used[k]==false and g[j][k]!=0)
f[i|g[j][k]]=min(f[i|g[j][k]],f[i]+1);
} printf("%lld\n",f[to]);
}
return 0;
}

[Luogu P2831] 愤怒的小鸟 (状压DP)的更多相关文章

  1. 洛谷P2831 愤怒的小鸟(状压dp)

    题意 题目链接 Sol 这题....我样例没过就A了??..算了,就当是样例卡精度吧.. 直接状压dp一下,\(f[sta]\)表示干掉\(sta\)这个集合里面的鸟的最小操作数 转移的时候判断一下一 ...

  2. 【题解】P2831 愤怒的小鸟 - 状压dp

    P2831愤怒的小鸟 题目描述 \(Kiana\) 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以 ...

  3. Luogu P2831 愤怒的小鸟(状压+记忆化搜索)

    P2831 愤怒的小鸟 题意 题目描述 Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于\((0,0)\)处,每次Kiana可以用它向第一象限发射 ...

  4. P2831 愤怒的小鸟 状压dp

    这个题主要是预处理比较复杂,先枚举打每只鸟用的抛物线,然后找是否有一个抛物线经过两只鸟,然后就没了. 题干: 题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上 ...

  5. NOIP2016愤怒的小鸟 [状压dp]

    愤怒的小鸟 题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟, ...

  6. [Luogu P3959] 宝藏 (状压DP+枚举子集)

    题面 传送门:https://www.luogu.org/problemnew/show/P3959 Solution 这道题的是一道很巧妙的状压DP题. 首先,看到数据范围,应该状压DP没错了. 根 ...

  7. luogu2831 [NOIp2016]愤怒的小鸟 (状压dp)

    由范围可以想到状压dp 两个点(再加上原点)是可以确定一个抛物线的,除非它们解出来a>=0,在本题中是不合法的 这样的话,我们可以预处理出由任意两个点确定的抛物线所经过的所有的点(要特别规定一下 ...

  8. [noip2016]愤怒的小鸟<状压dp+暴搜>

    题目链接:https://vijos.org/p/2008 现在回过头去看去年的考试题,发现都不是太难,至少每道题都有头绪了... 这道题的数据范围是18,这么小,直接暴力呗,跑个暴搜就完了,时间也就 ...

  9. NOIP2016Day2T3愤怒的小鸟(状压dp) O(2^n*n^2)再优化

    看这范围都知道是状压吧... 题目大意就不说了嘿嘿嘿 网上流传的写法复杂度大都是O(2^n*n^2),这个复杂度虽然官方数据可以过,但是在洛谷上会TLE[百度搜出来前几个博客的代码交上去都TLE了], ...

随机推荐

  1. Centos-网络下载文件-wget

    wget 指定URL从网络上下载某个文件,需要网络连接 相关选项 -nc 不覆盖同名文件 -q    安静下载,无输出 -v    显示下载详情 -O   指定保存目录或重命名下载文件名 -c 断点续 ...

  2. Python-为元组中每个元素命名

    学生信息系统: (名字,年龄,性别,邮箱地址) 为了减少存储开支,每个学生的信息都以一个元组形式存放 如: ('tom', 18,'male','tom@qq.com' ) ('jom', 18,'m ...

  3. 1.变量:var,let,const

    var在ECMAScript的所有版本中都可以使用,而const和let只能在ES6及更晚的版本中使用. var,let,const三个关键字的区别 var: 1)声明作用域:在函数内部,使用var定 ...

  4. 可变参数__VA_ARGS__使用

    1. 调试功能一般会使用到宏+可变参数的方式 1.1 ##__VA_ARGS__      之详细解析 例如: case A. #define my_print1(...)    printf(__V ...

  5. 生成器generator和迭代器Iterator

    一.列表生成式       在学习生成器迭代器之前先了解一下什么是列表生成式,列表生成式是Python内置的非常简单却强大的可以用来创建list的生成式.什么意思?举个例子,如果想生成列表[0,1,2 ...

  6. 关于继承、封装、多态、抽象和接口(Java)

    1.继承:    通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类.已有的称为父类,新的类称为子类(父类派生子类,子类继承父类). (1)继承的优点:①代码的可重用性: ②父类的属性的方 ...

  7. 《穷查理年鉴》诚实 & 希望 & 勇气 & 失败 & 改变(关于美好)

    诚实 013.自欺是最常见的欺骗. 038.毫无顾忌进行欺骗的人是无所畏惧的. 043.问心无愧者永无所惧. 068.诚实的人从欺诈人手中得到的买卖,我们从争论中得到真理. 134.没有欺骗就没有信任 ...

  8. linux块设备驱动---相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  9. 多测师讲解selenium _assert断言_高级讲师肖sir

    assert断言 # # 断言:最常用的断言方法if判断# assert Python语法中自带的断言from selenium import webdriverfrom time import sl ...

  10. JVM系列【6】GC与调优1

    JVM系列笔记目录 虚拟机的基础概念 class文件结构 class文件加载过程 jvm内存模型 JVM常用指令 GC与调优 GC基础知识 什么是垃圾 ​ 没有任何引用指向的一个对象或多个对象(循环引 ...