zoukankan      html  css  js  c++  java
  • 「NOIP2016」愤怒的小鸟

    「NOIP2016」愤怒的小鸟


    如果markdown无法显示,请更新你的浏览器

    Description

    (Kiana) 最近沉迷于一款神奇的游戏无法自拔。
    简单来说,这款游戏是在一个平面上进行的。有一架弹弓位于 ((0,0)) 处,每次 (Kiana) 可以用它向第一象限发射一只小鸟,小鸟们的飞行轨迹均为形如(y=ax^2+bx)的曲线,其中 (a)(b)(Kiana) 指定的参数,且必须满足 (a<0)。当小鸟落回地面(即 (x) 轴)时,它就会瞬间消失。
    在游戏的某个关卡里,平面的第一象限中有 (n) 只猪,其中第 (i) 只猪所在的坐标为 ((x_i,y_i))。如果某只小鸟的飞行轨迹经过了((x_i,y_i)),那么第 (i)只猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;如果一只小鸟的飞行轨迹没有经过((x_i,y_i)),那么这只小鸟飞行的全过程就不会对第(i)只猪产生任何影响。
    例如,若两只猪分别位于((1,3))((3,3))(Kiana) 可以选择发射一只飞行轨迹为 (y=−x^2+4x) 的小鸟,这样两只猪就会被这只小鸟一起消灭。
    而这个游戏的目的,就是通过发射小鸟消灭所有的猪。
    这款神奇游戏的每个关卡对来说都很难,所以 (Kiana)还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在「输入格式」中详述。
    假设这款游戏一共有 (T) 个关卡,现在(Kiana)想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的猪。由于她不会算,所以希望由你告诉她。


    Input Format

    下面依次输入这 T 个关卡的信息。每个关卡第一行包含两个非负整数 (n)(m) ,分别表示该关卡中的小猪数量和 (Kiana) 输入的神秘指令类型。接下来的 (n) 行中,第 (i) 行包含两个正实数 (x_i)(y_i),表示第 (i) 只小猪坐标为 ((x_i,y_i))。数据保证同一个关卡中不存在两只坐标完全相同的小猪。

    如果 (m=0),表示 (Kiana) 输入了一个没有任何作用的指令。
    如果 (m=1) ,则这个关卡将会满足:至多用(lceil n/3+1 ceil)只小鸟即可消灭所有小猪。
    如果 (m=2) ,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 $lfloor n/3 floor $ 只小猪。
    保证 (1≤n≤18)(0≤m≤2)(0<x_i,y_i<10),输入中的实数均保留到小数点后两位。
    上文中,符号 分别表示对 c 向上取整和向下取整,例如:
    (lceil 2.1 ceil = lceil 2.9 ceil = lceil 3.0 ceil = lfloor 3.0 floor = lfloor 3.1 floor = lfloor 3.9 floor = 3)


    Output Format

    对每个关卡依次输出一行答案。
    输出的每一行包含一个正整数,表示相应的关卡中,消灭所有猪最少需要的小鸟数量。

    simple 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
    

    simple 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
    3
    

    simple 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
    

    输出

    6
    

    Hint

    样例解释 1

    这组数据中一共有两个关卡。

    第一个关卡与「问题描述」中的情形相同,(2) 只猪分别位于 ((1.00,3.00))((3.00,3.00)),只需发射一只飞行轨迹为 (y=−x^2+4x) 的小鸟即可消灭它们。

    第二个关卡中有 (5) 只猪,但经过观察我们可以发现它们的坐标都在抛物线 (y=−x^2+6x) 上,故 (Kiana) 只需要发射一只小鸟即可消灭所有猪。

    数据范围

    测试点 (1∼14:2≤n≤12,1≤T≤30)

    测试点 (15∼20:2≤n≤18,1≤T≤5)


    Solution

    首先我们 (n≤18) 一看就知道是搜索或者是状压

    但是这题我们考虑用状压dp来写

    首先根据状压的尿性,我们首先要设计一个状态

    我们用(a[i][j])来表示一只能打到第(i)只猪和第(j)只猪的鸟能打到多少只猪

    所以每次我们枚举出这一只鸟以后我们可以再用一个(k) 循环来判断还有哪些猪能被这只鸟打到

    如果第(k)只猪也能被这只鸟打到,那么(a[i][j]|=(1<<k))(第几位是(1)就表示第几只猪能被这只鸟打到)

    但是,问题来了,我们应该如何找出这只鸟呢?

    首先,就像题目说的每只鸟说经过的弧线都可以表示成(y=ax^2+bx)

    所以我们可以根据题目,列出下面的方程
    在看下面的解释之前,请你先会逆矩阵求法,矩阵乘法等数学基础

    $ y_1=ax_1^2+bx_1( )y_2=ax_2^2+bx_2$

    然后我们把它变成矩阵,就有

    $ left[
    egin{matrix}
    y_1
    y_2
    end{matrix}
    ight]
    $ (=) (left[ egin{matrix} x_1^2& x1\ x_2^2&x2\ end{matrix} ight]) (×) (left[ egin{matrix} a\ b\ end{matrix} ight])

    然后就有

    $ left[
    egin{matrix}
    a
    b
    end{matrix}
    ight]
    $ (=) (left[ egin{matrix} x_1^2 & x_1\ x_2^2 & x_2\ end{matrix} ight]^{-1}) (×) (left[ egin{matrix} y_1\ y_2\ end{matrix} ight])

    因为

    (left[ egin{matrix} x_1^2&x_1\ x_2^2&x_2\ end{matrix} ight]) (=) (x_1^2x_2-x_1x_2^2=(x_1-x_2)x_1x_2)

    所以有:
    (left[ egin{matrix} x_1^2&x_1\ x_2^2&x_2\ end{matrix} ight]^{-1}) (=) (frac{1}{(x_1-x_2)x_1x_2}) (left[ egin{matrix} x_2& -x_1\ -x_2^2&x_1^2\ end{matrix} ight] )

    (逆矩阵求法)

    于是:
    $ left[
    egin{matrix}
    a
    b
    end{matrix}
    ight]
    $ (=) (frac{1}{(x_1-x_2)x_1x_2}) ( left[ egin{matrix} x_2&-x_1\ -x_2^2&x_1^2\ end{matrix} ight] ) (×) (left[ egin{matrix} y_1\ y_2\ end{matrix} ight] )

    最后,我们再把矩阵拆开:

    (a=frac{1}{(x_1-x_2)x_1x_2}×(x_2y_1-x_1y_2))
    (b=frac{1}{(x_1-x_2)x_1x_2}×(x_1^2y_2-x_2^2y_1))

    最后我们就求出了(a)(b)
    因为(x_1) (x_2) (y_1) (y_2)可以通过枚举知道
    然后我们枚举所有状态,再用所有的抛物线去枚举这个状态,假设这个状态为(i),这个抛物线能消灭的猪是(a[Start][j]),其中(Start)开始的位置我们可以用一个(while)语句来查找,那么用这个抛物线消灭之后变成的状态就是(a[Start][j]),其中(|)是按位或运算。dp方程为(f[i|a[Start][j]]=min(f[i|a[Start][j]],f[i]+1))

    最后输出答案(f[(1<<n)-1])
    所以本题就结束了......


    代码

    #include<bits/stdc++.h>
    #define Re register int
    using namespace std;
    int N,M,T,a[20][20],f[1<<20],Start;
    double A,B;
    struct info{
    	double x,y;
    }Pig[20];
    double fabs(double x){return x>0?x:-x;}
    int main(){
    	scanf("%d",&T);
    	while (T--){
    		memset(f,0x3f,sizeof(f));
    		memset(a,0,sizeof(a));
    		scanf("%d%d",&N,&M);
    		for (Re i=0; i<N; ++i) scanf("%lf%lf",&Pig[i].x,&Pig[i].y);
    		for (Re i=0; i<N; ++i) a[i][i]|=(1<<i);
    		for (Re i=0; i<N; ++i)
    		for (Re j=i+1; j<N; ++j)
    			if (fabs(Pig[i].x-Pig[j].x)>1e-6){
    				A=(Pig[j].x*Pig[i].y-Pig[i].x*Pig[j].y)/((Pig[i].x-Pig[j].x)*Pig[i].x*Pig[j].x);
    				B=(Pig[i].y-A*Pig[i].x*Pig[i].x)/Pig[i].x;
    				if (A>-1e-6) continue;
    				for (Re k=0; k<N; ++k) if (fabs(A*Pig[k].x*Pig[k].x+B*Pig[k].x-Pig[k].y)<=1e-6) a[i][j]|=(1<<k); 
    			}
    		f[0]=0;
    		for (Re i=0; i<1<<N; ++i){
    			Start=0;
    			while ((i>>Start)&1) ++Start;
    			for (Re j=Start; j<N; ++j) f[i|a[Start][j]]=min(f[i|a[Start][j]],f[i]+1);
    		}
    		printf("%d
    ",f[(1<<N)-1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    操作系统学习笔记:银行家算法的回顾和训练
    操作系统学习笔记:内存学习随笔
    操作系统笔记:内存的连续管理
    操作系统笔记:内存的离散管理
    操作系统:内存管理复习ing之页面置换算法
    马原学习日记1:实践
    bootstrap简单教程
    css-6(媒体识别)
    css-5(弹性盒子)
    css-3(旋转+过渡)
  • 原文地址:https://www.cnblogs.com/to-the-end/p/11331817.html
Copyright © 2011-2022 走看看