洛谷【P2831】愤怒的小鸟 (NOIP 2016 D2T3)
2017-08-10 11:42
330 查看
题目描述
Kiana最近沉迷于一款神奇的游戏无法自拔。简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如y=ax2+bx的曲线,其中a,b是Kiana指定的参数,且必须满足a<0。
当小鸟落回地面(即x轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有n只绿色的小猪,其中第i只小猪所在的坐标为(xi,yi)。
如果某只小鸟的飞行轨迹经过了(xi,yi),那么第i只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过(xi,yi),那么这只小鸟飞行的全过程就不会对第i只小猪产生任何影响。
例如,若两只小猪分别位于(1,3)和(3,3),Kiana可以选择发射一只飞行轨迹为y=−x2+4x的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
这款神奇游戏的每个关卡对Kiana来说都很难,所以Kiana还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。
假设这款游戏一共有T个关卡,现在Kiana想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。
输入输出格式
输入格式:
第一行包含一个正整数T,表示游戏的关卡总数。下面依次输入这T个关卡的信息。每个关卡第一行包含两个非负整数n,m,分别表示该关卡中的小猪数量和Kiana输入的神秘指令类型。接下来的n行中,第i行包含两个正实数(xi,yi),表示第i只小猪坐标为(xi,yi)。数据保证同一个关卡中不存在两只坐标完全相同的小猪。
如果m=0,表示Kiana输入了一个没有任何作用的指令。
如果m=1,则这个关卡将会满足:至多用⌈n3+1⌉只小鸟即可消灭所有小猪。
如果m=2,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少⌊n3⌋只小猪。
保证1<=n<=18,0<=m<=2,0
输出格式:
对每个关卡依次输出一行答案。输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量
输入输出样例
输入样例#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=−x2+4x的小鸟即可消灭它们。
第二个关卡中有5只小猪,但经过观察我们可以发现它们的坐标都在抛物线 y=−x2+6x上,故Kiana只需要发射一只小鸟即可消灭所有小猪。
数据范围
题解
0.题目大意:给出第一象限的若干点,用若干条过原点的抛物线把它们完全覆盖,求最小的抛物线条数。
1.状态压缩DP
这道题数据范围较小,我们首先会想到暴搜。任选两个小猪,确定一条抛物线,然后把所有在抛物线上的小猪都打掉。把所有情况枚举一遍,取最小值。这样暴搜的复杂度是O(2nn<1364e
span class="mn" id="MathJax-Span-612" style="font-size: 70.7%; font-family: MathJax_Main;">3T)。而218⋅183⋅30=45864714240,时间复杂度太高。2n是枚举所有情况的复杂度,已经无法优化。那我们只能从n3入手。用状压dp,通过一个n3的预处理,可以把复杂度降到O(2nnT)。具体方法是用一个大的二进制数s来代表当前小猪的状态,1代表小猪被干掉了,0代表小猪没被干掉。比如(13)10=(1101)2代表第1、3、4只小猪被干掉了。
状态设计:F[s]使当前小猪的状态是s所需的最小小鸟数
状态转移:F[s|t]=min(F[s|t],F[s]+1)
初值:全赋成n(当m=1时可赋成⌈n3+1⌉)
s表示已经当前干掉的小猪,t表示下一步能干掉的小猪,‘|’是按位或运算。要注意两个细节:
1.舍弃两个点横坐标相等的情况
2.舍弃a≥0的情况(因为此时小鸟会直接冲到地上或沿直线飞出去233)
代码:
记忆化搜索版:#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #define N 20 #define S (1<<N) #define eps 1e-9 using namespace std; double x ; double y ; int dp [S]; int g ;//g[i][j]表示干掉i和j的同时还能干掉哪些小猪 int n,m,t,ts; int ans; inline bool equ(double x,double y)//防止精度误差 { return fabs(x-y)<eps; } void dfs(int x,int y,int z)//x表示当前搜到的小猪编号,y代表当前状态,z代表已经用的小鸟数 { if(x==n) {ans=min(ans,z);return;} if(z>ans) return;//剪枝 if(z>=dp[x][y]) return;//记忆化 dp[x][y]=z; if((1<<x)&y) dfs(x+1,y,z);//如果当前小猪已被干掉,直接搜下一头小猪 else { bool double_kill=false; for(int i=x+1;i<n;i++) if(!((1<<i)&y)) double_kill=true,dfs(x+1,y|g[x][i],z+1); if(!double_kill) dfs(x+1,y|(1<<x),z+1); } } void work() { memset(g,0,sizeof(g)); scanf("%d%d",&n,&m); ts=1<<n;ans=n; for(int i=0;i<n;i++) for(int j=0;j<ts;j++) dp[i][j]=n; for(int i=0;i<n;i++) scanf("%lf%lf",&x[i],&y[i]); for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) if(!equ(x[i],x[j]))//舍弃横坐标相等的情况 { double b=(y[i]-y[j]*(x[i]*x[i])/(x[j]*x[j]))/(x[i]-(x[i]*x[i])/x[j]); double a=(y[i]-x[i]*b)/(x[i]*x[i]); if(a>-eps) continue;//若a>=0,直接舍弃 g[i][j]=(1<<i)|(1<<j); for(int k=j+1;k<n;k++) if(equ(y[k],a*x[k]*x[k]+b*x[k])) g[i][j]|=1<<k; } dfs(0,0,0); printf("%d\n",ans); } int main() { scanf("%d",&t); while(t--) work(); return 0; }
循环版:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cstdlib> #include <cmath> #define N 20 #define S 1<<N #define eps 1e-9 using namespace std; double x ; double y ; int g ; int mk[S];//mk表示小猪的编号 int dp[S]; int t,n,m; inline bool equ(double x,double y) { return fabs(x-y)<eps; } void init()//预处理 { memset(g,0,sizeof(g)); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%lf%lf",&x[i],&y[i]); for(int i=0;i<n;i++) { mk[1<<i]=i; g[i][g[i][0]=1]=1<<i; for(int j=i+1;j<n;j++) if(!equ(x[i],x[j])) { int r,ok,k; double b=(y[i]-y[j]*(x[i]*x[i])/(x[j]*x[j]))/(x[i]-(x[i]*x[i])/x[j]); double a=(y[i]-x[i]*b)/(x[i]*x[i]); if(a>-eps) continue; for(r=(1<<i)|(1<<j),k=0;k<n;k++) if(equ(a*x[k]*x[k]+b*x[k],y[k])) r|=(1<<k); for(ok=0,k=1;k<=g[i][0]&&!ok;k++) ok=(g[i][k]&r)==r;//剔除重复的状态 if(!ok) g[i][++g[i][0]]=r; } } } void solve()//dp主过程 { int ts=1<<n;//总状态数 for(int i=1;i<ts;i++) dp[i]=n;//初始化 for(int i=0;i<ts;i++) { int l=(ts-1)^i;l=mk[l&(-l)];//lowbit for(int j=1;j<=g[l][0];j++) dp[i|g[l][j]]=min(dp[i|g[l][j]],dp[i]+1); } printf("%d\n",dp[ts-1]); } int main() { scanf("%d",&t); while(t--) {init();solve();} return 0; }
2.玄学做法
除了状压dp之外,还有一个玄学做法。就是把坐标数组随机打乱R次,每次按照顺序把小猪依次干掉,求所有小鸟数的最小值。时间复杂度O(n3TR)。在考场上为了求稳R可以取20000左右。然而,经过丧心病狂的实验,我发现R取46就能过,而且跑的飞快233。打乱的话可以用STL的random_shuffle函数,不要忘了更新随机种子。代码:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #define maxn 20 #define eps 1e-9 using namespace std; struct Point{ double x,y; }p[maxn]; bool vis[maxn]; int g[maxn][maxn]; int n,m,t; inline bool equ(double x,double y) { return fabs(x-y)<eps; } void work() { scanf("%d%d",&n,&m); int ans=n; for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); int R=46; while(R--) { int tot=0; memset(vis,false,sizeof(vis)); random_shuffle(p+1,p+n+1); for(int i=1;i<=n;i++) if(!vis[i]) { vis[i]=true; for(int j=i+1;j<=n;j++) if(!vis[j]) { if(equ(p[i].x,p[j].x)) continue; double b=(p[i].y-p[j].y*(p[i].x*p[i].x)/(p[j].x*p[j].x))/(p[i].x-(p[i].x*p[i].x)/p[j].x); double a=(p[i].y-p[i].x*b)/(p[i].x*p[i].x); if(a>=-eps) continue; vis[j]=true; for(int k=j+1;k<=n;k++) if(equ(a*p[k].x*p[k].x+b*p[k].x,p[k].y)) vis[k]=true; break; } tot++; } ans=min(ans,tot); if(ans==1) break; } printf("%d\n",ans); } int main() { srand(time(NULL)); scanf("%d",&t); while(t--) work(); return 0; }
R=23333时:
R=46时:
后记:我好像用LaTeX用上瘾了233
相关文章推荐
- 洛谷P2831 [Noip2016]愤怒的小鸟
- 【洛谷】2831 [Noip2016]愤怒的小鸟 状压DP
- 洛谷 2831 [NOIP2016] 愤怒的小鸟 状压bfs
- 【NOIP2016】洛谷2831 愤怒的小鸟
- [LUOGU]2831 愤怒的小鸟 [NOIP]2016 D2T3
- NOIP2016提高组day2 愤怒的小鸟
- [NOIP2016][状压DP]愤怒的小鸟
- [luogu2831][noip d2t3]愤怒的小鸟_状压dp
- 【状压DP】UOJ#265 【NOIP2016】愤怒的小鸟
- NOIP 2016 Senior 6 - 愤怒的小鸟
- NOIP 2016 Day2 T3 愤怒的小鸟
- 【NOIP2016】愤怒的小鸟
- NOIP 2016 愤怒的小鸟
- NOIP2016 愤怒的小鸟 (状压DP)
- [NOIP2016]愤怒的小鸟 状态压缩dp
- 【NOIP】提高组2016 愤怒的小鸟
- NOIP2016 愤怒的小鸟
- JZOJsenior4908.【NOIP2016提高组复赛】愤怒的小鸟
- [NOIP2016] 愤怒的小鸟
- 【NOIP2016】【CJOJ2257】2257 愤怒的小鸟