NOIP2016愤怒的小鸟 [状压dp]
愤怒的小鸟
题目描述
Kiana 最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 y=ax^2+bx的曲线,其中 a,b是Kiana 指定的参数,且必须满足 a<0,a,b 都是实数。
当小鸟落回地面(即 x 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 n 只绿色的小猪,其中第 i 只小猪所在的坐标为 (xi,yi) 。
如果某只小鸟的飞行轨迹经过了 (xi,yi) ,那么第 i 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过 (xi,yi) ,那么这只小鸟飞行的全过程就不会对第 i 只小猪产生任何影响。
例如,若两只小猪分别位于 (1,3) 和 (3,3) ,Kiana 可以选择发射一只飞行轨迹为 y=-x^2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
这款神奇游戏的每个关卡对 Kiana来说都很难,所以Kiana还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。
假设这款游戏一共有 T 个关卡,现在 Kiana想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。
输入输出格式
输入格式:
第一行包含一个正整数 T ,表示游戏的关卡总数。
下面依次输入这 T 个关卡的信息。每个关卡第一行包含两个非负整数 n,mn,m ,分别表示该关卡中的小猪数量和 Kiana 输入的神秘指令类型。接下来的 n 行中,第 ii 行包含两个正实数 xi,yi ,表示第 i 只小猪坐标为 (xi,yi)。数据保证同一个关卡中不存在两只坐标完全相同的小猪。
如果 m=0 ,表示Kiana输入了一个没有任何作用的指令。
如果 m=1 ,则这个关卡将会满足:至多用 ⌈n/3+1⌉ 只小鸟即可消灭所有小猪。
如果 m=2 ,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 ⌊n/3⌋ 只小猪。
保证 1≤n≤18 , 0≤m≤2 , 0<xi,yi<10 ,输入中的实数均保留到小数点后两位。
上文中,符号 ⌈c⌉ 和 ⌊c⌋ 分别表示对 c 向上取整和向下取整,例如: ⌈2.1⌉=⌈2.9⌉=⌈3.0⌉=⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3 。
输出格式:
对每个关卡依次输出一行答案。
输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量。
输入输出样例
输入样例#1:
2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00
输出样例#1:
1
1
输入样例#2:
3
2 0
1.41 2.00
1.73 3.00
3 0
1.11 1.41
2.34 1.79
2.98 1.49
5 0
2.72 2.72
2.72 3.14
3.14 2.72
3.14 3.14
5.00 5.00
输出样例#2:
2
2
3
输入样例#3:
1
10 0
7.16 6.28
2.02 0.38
8.33 7.78
7.68 2.09
7.46 7.86
5.77 7.44
8.24 6.72
4.42 5.11
5.42 7.79
8.15 4.99
输出样例#3:
6
说明
【样例解释1】
这组数据中一共有两个关卡。
第一个关卡与【问题描述】中的情形相同,2只小猪分别位于 (1.00,3.00) 和 (3.00,3.00) ,只需发射一只飞行轨迹为 y = -x^2 + 4x 的小鸟即可消灭它们。
第二个关卡中有 5 只小猪,但经过观察我们可以发现它们的坐标都在抛物线 y = -x^2 + 6x 上,故Kiana只需要发射一只小鸟即可消灭所有小猪。
【数据范围】

题解
这道题有三种做法:强力剪枝搜索,记忆化搜索,状压dp,这里主要讲状压dp的做法
题目要求我们用最少的小鸟击落所有的猪,而小鸟的运动轨迹是一条抛物线,那么我们就想可不可以枚举两个不同的小鸟来来确定这条抛物线呢?这是肯定的,因为这条抛物线只有两个参数a,b,我们将两个小鸟的坐标带入进去就可以求得这两个参数,解二元一次方程,这应该是初中数学内容
\(a*x_1^2+b*x_1=y_1\)
\(a*x_2^2+b*x_2=y_2\)
得到
\(a=(x_2*y_1-x_1*y_2)/(x_1^2*x_2-x_2^2*x_1);\)
\(b=(x_1^2*y_2-x_2^2*y_1)/(x_1^2*x_2-x_2^2*x_1);\)
然后要注意\(x_1^2*x_2-x_2^2*x_1!=0\),即分母不能为0
求出这条抛物线后,我们就可以预处理这条抛物线打下了哪些猪,可以用一个二位数组f[i][j]保存通过枚举i和j这两个猪所构成的抛物线打下了哪些猪
注意:题目要求a<0,所以碰到a>0的情况就要舍去,fabs是取浮点数的绝对值,虽然abs也可用但是慢很多
然后就是标准的状压dp,状态转移方程为dp[sta|f[i][j]]=min(dp[sta|f[i][j]],dp[sta]+1),这里的f[i][j]指的是是合法的抛物线
#include<bits/stdc++.h>
#define rg register
#define il inline
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define lol long long
using namespace std;
const int N=20;
const double eps=1e-10;
void in(int &ans) {
ans=0; int f=1; char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
ans*=f;
}
int T,n,m;
double a,b,x[N],y[N];
int dp[1<<18],s[N][N];
il void clear() {
a=b=0;
memset(dp,0x3f,sizeof(dp));
memset(s,0,sizeof(s));
}
bool check(int i,int j) {
if(fabs((x[j]-x[i])/(x[i]*x[j]))<=eps) return 0;
a=(x[i]*y[j]-x[j]*y[i])/(x[j]-x[i])/(x[i]*x[j]);
b=(x[i]*x[i]*y[j]-x[j]*x[j]*y[i])/(x[i]*x[i]*x[j]-x[j]*x[j]*x[i]);
return (a<0);
}
void read() {
in(n),in(m);
for(rg int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
}
void work() {
for(rg int i=1;i<=n;i++) {
for(rg int j=1;j<=n;j++) {
if(fabs(x[i]-x[j])<=eps) continue;
if(!check(i,j)) continue;
s[i][j]|=(1<<i-1)|(1<<j-1);
for(rg int k=1;k<=n;k++) {
if(i==k || j==k) continue;
if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<=eps) s[i][j]|=1<<k-1;
}
}
}
}
void DP() {
int tot=1<<n; dp[0]=0;
for(rg int sta=0;sta<tot;sta++) {
for(rg int i=1;i<=n;i++) {
if(sta&(1<<i-1)) continue;
for(rg int j=1;j<=n;j++) {
if(!s[i][j]) continue;
if(sta&(1<<j-1)) continue;
if(fabs(x[i]-x[j])<=eps) continue;
dp[sta|s[i][j]]=Min(dp[sta|s[i][j]],dp[sta]+1);
}
dp[sta|(1<<i-1)]=Min(dp[sta|(1<<i-1)],dp[sta]+1);
}
}
printf("%d\n",dp[(1<<n)-1]);
}
int main()
{
in(T);
while(T--) {
clear(),read();
work(),DP();
}
return 0;
}
NOIP2016愤怒的小鸟 [状压dp]的更多相关文章
- luogu2831 [NOIp2016]愤怒的小鸟 (状压dp)
由范围可以想到状压dp 两个点(再加上原点)是可以确定一个抛物线的,除非它们解出来a>=0,在本题中是不合法的 这样的话,我们可以预处理出由任意两个点确定的抛物线所经过的所有的点(要特别规定一下 ...
- [noip2016]愤怒的小鸟<状压dp+暴搜>
题目链接:https://vijos.org/p/2008 现在回过头去看去年的考试题,发现都不是太难,至少每道题都有头绪了... 这道题的数据范围是18,这么小,直接暴力呗,跑个暴搜就完了,时间也就 ...
- [Luogu P2831] 愤怒的小鸟 (状压DP)
题面: 传送门:https://www.luogu.org/problemnew/show/P2831 Solution 首先,我们可以先康一康题目的数据范围:n<=18,应该是状压或者是搜索. ...
- 洛谷P2831 愤怒的小鸟(状压dp)
题意 题目链接 Sol 这题....我样例没过就A了??..算了,就当是样例卡精度吧.. 直接状压dp一下,\(f[sta]\)表示干掉\(sta\)这个集合里面的鸟的最小操作数 转移的时候判断一下一 ...
- NOIP2016Day2T3愤怒的小鸟(状压dp) O(2^n*n^2)再优化
看这范围都知道是状压吧... 题目大意就不说了嘿嘿嘿 网上流传的写法复杂度大都是O(2^n*n^2),这个复杂度虽然官方数据可以过,但是在洛谷上会TLE[百度搜出来前几个博客的代码交上去都TLE了], ...
- 【题解】P2831 愤怒的小鸟 - 状压dp
P2831愤怒的小鸟 题目描述 \(Kiana\) 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以 ...
- P2831 愤怒的小鸟 状压dp
这个题主要是预处理比较复杂,先枚举打每只鸟用的抛物线,然后找是否有一个抛物线经过两只鸟,然后就没了. 题干: 题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上 ...
- [NOIP2016]愤怒的小鸟 D2 T3 状压DP
[NOIP2016]愤怒的小鸟 D2 T3 Description Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可 ...
- Noip2016愤怒的小鸟(状压DP)
题目描述 题意大概就是坐标系上第一象限上有N只猪,每次可以构造一条经过原点且开口向下的抛物线,抛物线可能会经过某一或某些猪,求使所有猪被至少经过一次的抛物线最少数量. 原题中还有一个特殊指令M,对于正 ...
随机推荐
- vue---day04
1. Node.js 1.1 介绍: - Node.js 是一个JavaScript运行环境,实质上是对Chrome V8引擎的封装. - Node.js 不是一个 JavaScript 框架,不同于 ...
- Spyder在windows下常用快捷键
块注释/反块注释:Ctrl+4/5 行注释/反行注释:Ctrl+1 代码提示:Tab 复制一行:Ctrl+Alt+↓/↑ 删除一行:Ctrl+D 运行:F5 全屏:F11 撤销:Ctrl+Z 反撤销: ...
- 生産管理(PP)
伝票系 製造指図 マスタ系 生産資源/治工具 作業区 能力 作業手順 作業バージョン 作業記録 需要予測プロファイル 計画手配 MRP レシピ その他 カスタマイズ系 BOM関連 製造指図確認 伝票系 ...
- fastDFS 上传 java源码
要想搭建fastDFS网上有相近的文章: 分布式文件系统 - FastDFS 在 CentOS 下配置安装部署 分布式文件系统 - FastDFS 配置 Nginx 模块及上传测试 首先下载fastd ...
- Xcode9新变化
http://www.cnblogs.com/lurenq/archive/2017/09/26/7594909.html
- c/c++不能返回局部对象和局部变量的指针或引用解释
在编写c/c++代码时,调用函数的书写让程序变得整洁易读,但是调用函数的返回值(局部变量的返回值,变量,结构体,数组等)也有注意事项.c/c++严禁返回局部变量的指针或引用. 其实函数的返回值的规则非 ...
- Python全栈 MongoDB 数据库(概念、安装、创建数据)
什么是关系型数据库? 是建立在关系数据库模型基础上的数据库,借助于集合代数等概念和方法来处理数据库中的数据, 同时也是一个被组织成一组拥有正式描述性的表格( ...
- libvirt保持虚拟机运行情况下修改名称
通过virsh list命令能看到虚拟机的列表: [root@compute2 ~]# virsh list Id 名称 状态 ------------------------------------ ...
- LeetCode 389——找不同
1. 题目 2. 解答 2.1. 方法一 将 s 和 t 转化为 Python 的列表,然后遍历列表 s 的元素,将它们从列表 t 中删除,最后列表 t 中会余下一个元素,即为所求. class So ...
- WebSocket 与 HTTP/2
个人笔记 WebSocket WebSocket 是一个双向通信协议,它在握手阶段采用 HTTP/1.1 协议(暂时不支持 HTTP/2). 握手过程如下: 首先客户端向服务端发起一个特殊的 HTTP ...