【luogu P2831 愤怒的小鸟】 题解

题目链接:https://www.luogu.org/problemnew/show/P2831

写点做题总结:dp,搜索,重在设计状态,状态设的好,转移起来也方便。

对于一条抛物线,三点确定。(0,0)是固定的,所以我们一条抛物线要用两只猪确定。再多的猪就只能用来判断是不是在这条抛物线上了。
于是我们把猪分成两种:在已有方程里的猪,单独的猪还没有确定方程。
那么对于一只猪,就会有被以前方程覆盖/和前面单独的猪构成新抛物线/自己单独。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 20;
const int INF = 0x7fffffff;
const double eps = 1e-8;
int n, m, ans, T;
double x[maxn], y[maxn], a[maxn], b[maxn], xx[maxn], yy[maxn];
void dfs(int c, int u, int v)//搜当前第c个,前面构造好了u个方程,单独的猪v个
{
    if(u + v >= ans) return;
    if(c > n)
    {
        ans = min(u + v, ans);
        return;
    }
    bool flag = 0;
    for(int i = 1; i <= u; i++)//被之前的经过
    {
        if(fabs(a[i]*x[c]*x[c] + b[i]*x[c] - y[c]) < eps)
        {
            dfs(c+1, u, v);
            flag = 1;
            break;
        }
    }
    if(!flag)//之前的不经过
    {
        for(int i = 1; i <= v; i++)//在前面找一个单独的构成抛物线
        {
            if(fabs(xx[i] - x[c]) < eps) continue;
            double nowa = (y[c]*xx[i]-x[c]*yy[i])/(x[c]*xx[i]*(x[c]-xx[i]));//计算这个抛物线
            double nowb = (xx[i]*xx[i]*y[c]-x[c]*x[c]*yy[i])/(x[c]*xx[i]*(xx[i]-x[c]));
            if(nowa < 0)
            {
                a[u+1] = nowa, b[u+1] = nowb;
                double nowx = xx[i], nowy = yy[i];
                for(int j = i; j < v; j++)
                {
                    xx[j] = xx[j+1];
                    yy[j] = yy[j+1];
                }
                dfs(c+1, u+1, v-1);

                for(int j = v; j > i; j--)
                {
                    xx[j] = xx[j-1];
                    yy[j] = yy[j-1];
                }
                xx[i] = nowx; yy[i] = nowy;
            }
        }
        xx[v+1] = x[c], yy[v+1] = y[c];
        dfs(c+1, u, v+1);
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++) scanf("%lf%lf",&x[i],&y[i]);
        ans = INF;
        dfs(1, 0, 0);
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/MisakaAzusa/p/9909864.html

时间: 2024-07-31 04:28:30

【luogu P2831 愤怒的小鸟】 题解的相关文章

P2831 愤怒的小鸟(状压dp)

P2831 愤怒的小鸟 我们先预处理出每个猪两两之间(设为$u,v$)和原点三点确定的抛物线(当两只猪横坐标相等时显然无解) 处理出$u,v$确定的抛物线一共可以经过多少点,记为$lines[u][v]$ 设$f[i]$表示已经被消灭的猪的集合为二进制表示为$i$时,需要的最小抛物线数 显然$f[0]=0$ $f[i|(1<<(u-1))]=min(f[i|(1<<(u-1)],f[i]+1)$(一条抛物线只串一个点) $f[i|lines[u][v]]=min(f[i|lines

P2831 愤怒的小鸟——状压

P2831 愤怒的小鸟 抛物线过原点,只要再找两个就能确定抛物线: 处理出两两之间的抛物线能过哪些点,状态压缩: 但是直接枚举每一条抛物线常数太大会T,所以我们需要预处理一个low_bit表示当前状态下第一个没选的,即是二进制下第一个不是1的位置: 因为我们早晚都要把它变成1,所以先处理他就可以达到要求: 注意精度问题: #include<cmath> #include<cstdio> #include<cstring> #include<algorithm>

Luogu P4014 分配问题 题解

闲扯 蒟蒻的第一道自己想出怎么建图的题!!虽然是一个没什么技术含量的图 想了想,还是写篇题解纪念一下. 题面 题面 Solution 要求最小费用和最大费用,同时限制了流量,考虑费用流. 虚拟一个超级源点,从这个点分别向 \(N\) 个任务连一条流量为 \(1\) ,费用为 \(0\) 的边. 虚拟一个超级汇点,才从 \(N\) 个物品分别向该点连一条流量为 \(1\) ,费用为 \(0\) 的边. 因为每个人只能做一件,且每个工作只能做一次,所以连的边流量都为一.而第 \(i\) 个人做第 \

邮递员送信(luogu 1629)题解

[问题描述] 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每次只能带一样东西.求送完这N-1样东西并且最终回到邮局最少需要多少时间. [样例输入] 5 10    2 3 5    1 5 5    3 5 6    1 2 8    1 3 8    5 3 4    4 1 8    4 5 3    3 5 6    5 4 2 [样例输出] 83

P2831 愤怒的小鸟

传送门 看到数据范围就知道是搜索或状压DP 算了一波复杂度搜索好像过不了极限数据 搞状压 设 f [ i ] 表示所有猪的状态为 i (二进制下1表示死了,0表示没死)时需要的最少发射次数 设 p [ i ] [ j ] 存经过第 i 只猪和第 j 只猪的抛物线经过的猪的状态(可以$n^2$预处理出来,解方程都会吧..) 找到第一个没死的猪 i ,然后枚举所有其他没死的猪 j ,进行转移: f [ i|p [ i ] [ j ] ] = min ( f [ i|p [ i ] [ j ] ],f

$Luogu P2029$ 跳舞 题解

一道不是十分水的\(dp\). 首先我们考虑\(dp\)方程的构造.起初我定义的状态是\(dp_{i,j}\)表示前\(i\)个格子,总共跳了\(j\)次的最大得分.但事实上它并不可以转移,因为我们不知道新的一轮操作从之间的哪个格子算起. 那么状态转移方程就出来了,我们把第一维改成本次跳到第\(i\)个格子上,包括本次在内总共跳了\(j\)次的最大得分,那么转移的时候,由于本次一定要跳到\(i\)上(如状态中所定义),所以不用分类讨论.方程就是:\[dp_{i,j}=\max\{dp_{k,j-

Luogu P1342 请柬 题解

差不多是Dijkstra的裸题吧... 这道题可以分为来回两个阶段. 去的时候很简单,直接用一次Dijkstra,然后统计答案. 回来的时候就有些巧妙了,虽然表面上是每个点回到起点,但是何尝不可将其看成从起点出发,逆着每个点过来的路去找一次每个点?所以只需要存边的时候处理一下,然后直接跑Dijkstra就行了. 附上代码. #include<bits/stdc++.h> #define clean(a,i) memset(a,i,sizeof(a)) #define ll long long

Luogu P2014 选课 题解报告

题目传送门 [题目大意] 有n门选修课,每一门课都有固定的学分$S_i$,每个学生可以选m门课.有些选修课有先修课,每一门课最多只有一门先修课,求能获得的最多学分. [思路解析] 设f[x][t]表示在以x结点为根的子树中选t门课能获得的最大学分,x的子结点集合为son[x],子结点个数为p,且对于x的第i个子结点son[i],以其为根结点的子树中选课数量为$C_i$,则转移方程为:$$f[x][t]=max(\sum_{i=1}^{p}f[son[i]][c[i]])+s[i](满足\sum_

luogu P3952 时间复杂度题解

显然这是一道大模拟 我们要做的就是读入一堆字符串,然后模拟这个循环. 定义某一层的复杂度为执行完这一层循环之后,消耗的复杂度. 某层循环的复杂度=\(max \{\)所有并列的下一层循环的复杂度\(\}\).通俗点说,就是在某层循环中有分支的时候,这一层的复杂度=\(max \{\)所有分支的复杂度\(\}+\)本层复杂度. 最后复杂度=\(max \{\)所有的第一层循环复杂度\(\}\) 考虑到会有分支,所以我们采用递归来实现(当然其本质是栈,但是我不会写). 由于要使程序不至于\(RE\)