• [NOIp 2016]愤怒的小鸟


    Description

    这里写图片描述

    Input

    Output

    Sample Input

    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

    Sample Output

    1
    1

    Sample Explanation

    HINT

    这里写图片描述

    题解

    暴力做法:

    1、因为三点可以确定一条抛物线,又必过原点,那么只需要再找两个点就能确定一条抛物线;

    2、枚举点对,求出抛物线方程,注意两点的$x$坐标不能相等,抛物线的二次项系数必须小于$0$;

    3、删掉在抛物线上的点,进入下一层继续枚举。

    60分算法:

    1、如果使用暴力,我们会删掉一些线段进入下一个状态,一个状态是指现在平面内还剩多少个点;

    2、注意到一点:从当前状态无论以什么方式删点,均不会影响之前状态到当前状态的决策。也就是说:在之前的状态时,我们只需要考虑,怎样删点来到达当前状态即可,而不用管当前状态下如何删点来到达下一个状态;

    3、这具备明显的无后效性,我们用一个二进制数$S$来表示一个状态,若$S$的第$i$位为$1$,则表示第$(i+1)$个点还存在于平面内,那么状态压缩的$DP$可以解决;

    4、同样对于每个状态枚举抛物线即可。

    100分算法:

    1、平面内有$n$个点,共$2^n$个状态,每次枚举抛物线还要检查每个点,需要$O(n^3)$复杂度;

    2、每次都要枚举抛物线经过的两个点,即抛物线必定会删去的两个点。既然我们的最终目标是将当前状态所有的点都删去,那么可以知道,删掉所有点的最优方案中,一定会有一条抛物线经过当前状态的第一个点(否则就没法删掉它了);

    3、那么我们只需要枚举经过当前状态第一个点的抛物线就可以(这么做一定不会有错误的决策)。

     1 #include <set>
     2 #include <map>
     3 #include <ctime>
     4 #include <cmath>
     5 #include <queue>
     6 #include <stack>
     7 #include <vector>
     8 #include <cstdio>
     9 #include <string>
    10 #include <cstring>
    11 #include <cstdlib>
    12 #include <iostream>
    13 #include <algorithm>
    14 #define x1 x[i]
    15 #define x2 x[j]
    16 #define y1 y[i]
    17 #define y2 y[j]
    18 #define LL long long
    19 #define Max(a, b) ((a) > (b) ? (a) : (b))
    20 #define Min(a, b) ((a) < (b) ? (a) : (b))
    21 using namespace std;
    22 const double ex=1e-8;
    23 int st[20] = {1};
    24 
    25 int n, m;
    26 double x[20], y[20];
    27 int c[20][20];
    28 int f[(1<<18)+5];
    29 
    30 void work(){
    31     scanf("%d%d", &n, &m);
    32     for (int i = 0; i < n; i++)
    33       scanf("%lf%lf", &x[i], &y[i]);
    34     memset(c, 0, sizeof(c));
    35     for (int i = 0; i < n; i++)
    36       for (int j = i+1; j < n; j++){
    37           double a = (x1*y2-y1*x2)/(x1*x2*x2-x1*x1*x2);
    38           if (a >= 0) continue;
    39           double b = (y1-a*x1*x1)/x1;
    40           for (int k = 0; k < n; k++)
    41             if (abs(y[k]-a*x[k]*x[k]-b*x[k]) <= ex)
    42                 c[i][j]+=st[k];
    43     }
    44     memset(f, 127, sizeof(f));
    45     f[0]=0;
    46     int lim = (1<<n)-1;
    47     int INF = f[1];
    48     for (int i = 0; i <= lim; i++)
    49       if (f[i] != INF)
    50           for (int j = 0; j < n; j++)
    51             if (!(i&st[j])){
    52                 f[i|st[j]] = Min(f[i|st[j]], f[i]+1);
    53                 for (int k = j+1; k < n; k++){
    54                   int tmp = i|c[j][k];
    55                   f[tmp] = Min(f[tmp], f[i]+1);
    56                 }
    57             }
    58     printf("%d
    ", f[lim]);
    59 }
    60 
    61 int main(){
    62     for (int i = 1; i <= 18; i++)
    63     st[i] = st[i-1]<<1;
    64     int t;
    65     scanf("%d", &t);
    66     while (t--)
    67       work();
    68     return 0;
    69 }
  • 相关阅读:
    数论 欧几里德算法 以及 欧几里得拓展
    数论 快速幂的原理讲解
    汉诺塔模板
    C++ 迭代器运算
    C++ STL vector set map 简易用法
    C++ 使用指向函数的指针数组
    Codeforces 718C 线段树+矩乘
    BZOJ 2506 分块
    Codeforces 455D 分块+链表
    Codeforces 19E 树上差分
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7475749.html
Copyright © 2020-2023  润新知