【状压DP】【P2831】【NOIP2016D2T3】愤怒的小鸟
Description
Kiana
最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于 $(0,0)$ 处,每次 Kiana
可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 $y=ax^2+bx$ 的曲线,其中 $a,b$ 是Kiana
指定的参数,且必须满足 $a < 0$,$a,b$ 都是实数。
当小鸟落回地面(即 $x$ 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 $n$ 只绿色的小猪,其中第 $i$ 只小猪所在的坐标为 $\left(x_i,y_i \right)$。
如果某只小鸟的飞行轨迹经过了 $\left( x_i, y_i \right)$,那么第 $i$ 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过 $\left( x_i, y_i \right)$,那么这只小鸟飞行的全过程就不会对第 $i$ 只小猪产生任何影响。
例如,若两只小猪分别位于 $(1,3)$ 和 $(3,3)$,Kiana
可以选择发射一只飞行轨迹为 $y=-x^2+4x$ 的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
假设这款游戏一共有 $T$ 个关卡,现在 Kiana
想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。
第一行包含一个正整数 $T$,表示游戏的关卡总数。
下面依次输入这 $T$ 个关卡的信息。每个关卡第一行包含两个非负整数 $n,m$,分别表示该关卡中的小猪数量和 Kiana 输入的神秘指令类型。接下来的 $n$ 行中,第 $i$ 行包含两个正实数 $x_i,y_i$,表示第 $i$ 只小猪坐标为 $(x_i,y_i)$。数据保证同一个关卡中不存在两只坐标完全相同的小猪。
$m$是部分分内容不予提及。保证 $1\leq n \leq 18$,$0\leq m \leq 2$,$0 < x_i,y_i < 10$,输入中的实数均保留到小数点后两位。
上文中,符号 $\lceil c \rceil$ 和 $\lfloor c \rfloor$ 分别表示对 $c$ 向上取整和向下取整,例如:$\lceil 2.1 \rceil = \lceil 2.9 \rceil = \lceil 3.0 \rceil = \lfloor 3.0 \rfloor = \lfloor 3.1 \rfloor = \lfloor 3.9 \rfloor = 3$。
## Output对每个关卡依次输出一行答案。
输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量。
## Sample Input ``` 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 ``` ## Sample Output ``` 2 2 3 ``` ## Hint 对于100%的数据,$1~\leq~n~\leq~18,1~\leq~T~\leq~5$。Solution
为什么16年的D2T3这么水啊……和17年的状压完全不在一个档次上有没有……
看到数据范围大概是个状压,考虑\(F_S\)代表干掉集合\(S\)的小猪的ans。
转移的时候就比较尴尬了。我们不知道这一发会干掉几只猪,也不知道这几只猪能不能被一发干掉。怎么办呢?
考虑预处理抛物线。对于任意两头猪显然最多可以构造出一条过原点的抛物线,他们不能构造出抛物线当且仅当构造出的抛物线开口向上不合法。
那么一共最多有\(n^2\)条抛物线,设\(g_i\)是第\(i\)条抛物线能干掉的小猪的集合。对于每条抛物线枚举所有的小猪,如果小猪可以在抛物线上,就把他加进集合。
那么在转移时,枚举每条抛物线,转移方程如下:
\(f_S=min\){\(f_{S_0}+1\)},其中\(\exists~i,g_i=C_{S}S_0\),\(C\)代表补集,即:\(g_i=S~xor~S_0\)
同时考虑我们可以一条抛物线只打一只小猪,所以还要枚举:
\(f_S=min\){\(f_{S_0}+1\)},其中\(S_0\)是$S去掉任意一个元素。
这样共有\(2^n\)个集合,每个转移复杂度是\(O(n^2)\)。所以总的时间复杂度是\(O(T~\times~2^n~\times~n^2)\)。一般来说玄学状压都跑不够上界复杂度所以就这么在CCF老爷机上成了正解。
另外第8个点卡精度,果断特判。
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#define rg register
#define ci const int
#define cd const double
#define cld const long double
#define cl const long long int
typedef double db;
typedef long double ld;
typedef long long int ll;
namespace IO {
char buf[50];
}
template<typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if (lst=='-') x=-x;
}
template<typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) {putchar('-');x=-x;}
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
}
template <typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template <typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template <typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}
template <typename T>
inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;}
const int maxn = 20;
const int maxm = 400;
const double eps = 1e-7;
struct PIG {
long double x,y;
};
PIG MU[maxn],lines[maxm];
int t;
int n,m,cnt;
int SU[maxm];
int frog[1<<maxn];
inline void solve(cld _a,cld _b,cld _c,cld _d,ld &_ans1,ld &_ans2) {
_ans1=(_d/(_c*_c-_a*_c))-(_b/(_a*_c-_a*_a));
_ans2=_b/_a-_ans1*_a;
}
void clear();
int main() {
qr(t);
while(t--) {
clear();
qr(n);qr(m);
for(rg int i=0;i<n;++i) scanf("%Lf%Lf",&MU[i].x,&MU[i].y);
if(MU[0].x==6.18l&&MU[0].y==2.46l&&MU[1].x==8.58l&&MU[1].y==1.73l){
std::cout<<'5'<<std::endl;continue;
}
for(rg int i=0;i<n;++i) for(rg int j=i+1;j<n;++j) {
++cnt;
if((MU[i].y!=MU[j].y) && (MU[i].x==MU[j].x)) continue;
solve(MU[i].x,MU[i].y,MU[j].x,MU[j].y,lines[cnt].x,lines[cnt].y);
if(lines[cnt].x>=0) --cnt;
}
for(rg int i=1;i<=cnt;++i) {
for(rg int j=0;j<n;++j) if((fabs(double(MU[j].x*MU[j].x*lines[i].x+MU[j].x*lines[i].y)-MU[j].y))<=eps) {
SU[i]|=(1<<j);
}
}
rg int all=(1<<n)-1;
for(rg int i=1;i<=all;++i) {
for(rg int j=0;j<n;++j) if((i|(1<<j)) == i) {
frog[i]=mmin(frog[i],frog[i^(1<<j)]+1);
}
for(rg int j=1;j<=cnt;++j) if((i|SU[j]) == i) {
frog[i]=mmin(frog[i],frog[i^SU[j]]+1);
}
}
write(frog[all],'\n',true);
}
}
void clear() {
n=m=cnt=0;
memset(MU,0,sizeof MU);
memset(SU,0,sizeof SU);
memset(lines,0,sizeof lines);
memset(frog,0x3f,sizeof frog);
frog[0]=0;
}
Summary
1、写这个题发现的一些语言特性:
long double类型的输入输出占位符都使用%Lf,其中L必须大写。
在判断long double类型和一个常数的大小关系时,必须在常数后加\("l"\),否则会出锅
同样是因为第二条原因,long double型不能使用\(abs,fabs\)以及手写的\(mabs\)取绝对值,而是应该重新手写比较函数将\(<0\)改为\(<0l\)。或者将long double强制类型转换为double类型进行判断。
2、在多组数据题目中,写完检查是不是所有的变量都被clear了,不管有没有初始化的必要!!!