码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-01-11 15:00:13      阅读:85      评论:0      收藏:0      [点我收藏+]

标签:play   输入   一个   完成   必须   假设   names   影响   com   

\(\large{P2831}\Large{愤怒的小鸟}\)

\(\large\text{题目描述}\)

\(Kiana\) 最近沉迷于一款神奇的游戏无法自拔。

简单来说,这款游戏是在一个平面上进行的。

有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 \(y=a*x^2+b*x\) 的曲线,其中 \(a,b\)\(Kiana\) 指定的参数,且必须满足 \(a<0\)\(a,b\) 都是实数。

当小鸟落回地面(即 \(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+4*x\) 的小鸟,这样两只小猪就会被这只小鸟一起消灭。

而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。

这款神奇游戏的每个关卡对 \(Kiana\) 来说都很难,所以 \(Kiana\) 还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。

假设这款游戏一共有 \(T\) 个关卡,现在 \(Kiana\) 想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。


· 按照初(数)步(据)想(范)法(围),可以很容易就想到设一个集合表示每只猪的状态 即 \(f_S\) 表示状态为 \(S\) 时最少抛物线数

· 可以发现由于必须过原点,所以只用再确定两只猪就可以确 定一条抛物线

· 于是我们可以预处理出 \(l_{i,j}\) 表示在 \(i\)\(j\) 所过的这条 抛物线上的所有猪的集合

· 这样子就很方便了

· 枚举 \(S\),对于每个 \(S\) 枚举 \(i\)\(j\) 则有 : \[f[S\ | \ l_{i,j}] = min(f[S\ |\ l_{i,j}], f_S + 1)\]

· 于是愉快地(?)完成了这道题了

· 算算复杂度...\(O(2^n*n^2)≈8*10^7\)...貌似只能卡常卡过去?有没有优化呢?

· 令 \(x\)\(S\) 内未被打掉的猪中编号最小的,则由 \(S\) 扩展的所有的线都要经过 \(x\)

· 这样子为什么是对的呢?

· 如果说先不打 \(x\),打了 \(x\) 之后的 \(y\)\(z\),那么总要返回来再打一次 \(x\),这样子转移就重复了(虽然仍然是正确的)

· 那么就只用枚举一个 \(j\) 就可以了,转移的速度是 \(O(n)\)的,那么总复杂度为 \(O(2^n*n)≈4*10^6\),顺利跑过啦~

· 顺带一提的是 这道题可以用爆搜+剪枝,而且貌似比dp还要快一点...有兴趣的可以想一想2333


#include<bits/stdc++.h>
#define ld long double
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
const int N = 20;
const double eps = 1e-8; //由于浮点数不太好比大小,所以如果两数之差小于这个超小值则算它们相等
int n, m, all, t;
struct pig{
    long double x, y;
}p[N];
int l[N][N];
int d[(1 << N)];
int f[(1 << N)];
int main()
{
    scanf("%d", &t);
    F(i, 0, 1 << 19)
        F(j, 1, 19) 
            if(! (i & (1 << j - 1)))
            {
                d[i] = j; 
                break;
            }
    while(t --)
    {
        scanf("%d%d", &n, &m), all = (1 << n) - 1;
        F(i, 1, n) scanf("%Lf%Lf", &p[i].x, &p[i].y);    
        memset(l, 0, sizeof(l));
        memset(f, 127, sizeof(f)), f[0] = 0;
        F(i, 1, n)
            F(j, 1, n) 
            {
                if(fabs(p[i].x - p[j].x) < eps) continue;
                ld a = (p[j].x * p[i].y - p[i].x * p[j].y) / (p[i].x * p[j].x * (p[i].x - p[j].x));
                ld b = p[i].y / p[i].x - a * p[i].x;
                if(a > -eps) continue;
                F(k, 1, n)
                    if(fabs(a * p[k].x * p[k].x + b * p[k].x - p[k].y) < eps)
                        l[i][j] |= (1 << k - 1);
            }
        F(i, 0, all)           
        {
            int j = d[i];
            f[i | (1 << j - 1)] = min(f[i | (1 << j - 1)], f[i] + 1);
            F(k, 1, n) f[i | l[j][k]] = min(f[i | l[j][k]], f[i] + 1);  
        }
        printf("%d\n", f[(1 << n) - 1]);
    }
    return 0;
}

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

标签:play   输入   一个   完成   必须   假设   names   影响   com   

原文地址:https://www.cnblogs.com/Bn-ff/p/12179668.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!