DP - 状压DP - NOIP2016 - 愤怒的小鸟

DP - 状压DP - NOIP2016 - 愤怒的小鸟

题意:

T 组 测 试 用 例 , 每 组 包 括 n 个 点 的 坐 标 。 T组测试用例,每组包括n个点的坐标。 Tn

现 需 要 用 过 原 点 的 抛 物 线 y = a x 2 + b x ( a < 0 ) , 来 覆 盖 这 n 个 点 , 问 至 少 需 要 多 少 条 抛 物 线 。 现需要用过原点的抛物线y=ax^2+bx(a<0),来覆盖这n个点,问至少需要多少条抛物线。 线y=ax2+bx(a<0)n线

输入样例:
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 < = n < = 18 , 0 < x i , y i < 10 。 时 / 空 限 制 : 2 s / 128 M B 1<=n<=18,0<x_i,y_i<10。\\时/空限制:2s / 128MB 1<=n<=180<xi,yi<10/2s/128MB


分析:

对 于 方 程 y = a x 2 + b x , 任 意 两 点 可 以 确 定 参 数 a 和 b , n 个 点 最 多 有 n 2 条 抛 物 线 。 对于方程y=ax^2+bx,任意两点可以确定参数a和b,n个点最多有n^2条抛物线。 y=ax2+bxabnn2线

问 题 转 化 为 从 n 2 条 抛 物 线 中 最 少 选 择 几 条 , 能 够 覆 盖 n 个 点 , 这 n 2 条 抛 物 线 可 以 先 暴 力 预 处 理 出 来 。 问题转化为从n^2条抛物线中最少选择几条,能够覆盖n个点,这n^2条抛物线可以先暴力预处理出来。 n2线nn2线

接 着 可 以 用 一 个 n 位 二 进 制 数 表 示 哪 些 点 已 经 被 覆 盖 了 , 对 于 没 有 被 覆 盖 的 点 x , 我 们 再 枚 举 所 有 能 够 覆 盖 x 的 抛 物 线 , 若 所 有 点 均 被 覆 盖 了 ( n 位 全 1 ) , 就 更 新 一 下 使 用 的 抛 物 线 条 数 。 接着可以用一个n位二进制数表示哪些点已经被覆盖了,对于没有被覆盖的点x,我们再枚举所有能够覆盖x的抛物线,\\若所有点均被覆盖了(n位全1),就更新一下使用的抛物线条数。 nxx线(n1)使线

解 参 数 a , b : 对 于 ( x 1 , y 1 ) 和 ( x 2 , y 2 ) 所 确 定 的 抛 物 线 , 有 方 程 组 { y 1 = a x 1 2 + b x 1 y 2 = a x 2 2 + b x 2 , 即 { y 1 x 1 = a x 1 + b y 2 x 2 = a x 2 + b , 两 式 作 差 , 得 { a = y 1 x 1 − y 2 x 2 x 1 − x 2   b = y 1 x 1 − a x 1 , ( x 1 ≠ x 2 且 x 1 ≠ 0 ) 。 解参数a,b:\\对于(x_1,y_1)和(x_2,y_2)所确定的抛物线,有方程组\begin{cases}y_1=ax_1^2+bx_1\\y_2=ax_2^2+bx_2\end{cases},即\begin{cases}\frac{y_1}{x_1}=ax_1+b\\\frac{y_2}{x_2}=ax_2+b\end{cases},\\两式作差,得\begin{cases}a=\frac{ \frac{y_1}{x_1}-\frac{y_2}{x_2} }{x_1-x_2} \\ \ \\b=\frac{y_1}{x_1}-ax_1\end{cases},(x_1≠x_2且x_1≠0)。 a,b(x1,y1)(x2,y2)线{y1=ax12+bx1y2=ax22+bx2{x1y1=ax1+bx2y2=ax2+b,a=x1x2x1y1x2y2 b=x1y1ax1,(x1=x2x1=0)

具体落实:

f [ s t a t e ] : 表 示 覆 盖 状 态 为 s t a t e 需 要 的 抛 物 线 条 数 。 p a t h [ i ] [ j ] : 表 示 点 i 和 点 j 所 确 定 的 抛 物 线 能 够 覆 盖 的 二 进 制 状 态 。 f[state]:表示覆盖状态为state需要的抛物线条数。\\path[i][j]:表示点i和点j所确定的抛物线能够覆盖的二进制状态。 f[state]:state线path[i][j]:ij线

① 、 预 处 理 p a t h 数 组 , 尤 其 注 意 两 个 点 的 横 坐 标 不 能 相 同 , 抛 物 线 开 口 要 朝 下 ( a < 0 ) 。 ①、预处理path数组,尤其注意两个点的横坐标不能相同,抛物线开口要朝下(a<0)。 path线(a<0)

② 、 D P 求 f 数 组 , 对 于 每 一 个 状 态 i , 先 任 选 一 个 该 状 态 还 未 覆 盖 到 的 点 x , 接 着 枚 举 所 有 覆 盖 x 点 的 抛 物 线 所 能 覆 盖 的 状 态 p a t h [ x ] [ j ] , j ∈ [ 0 , n − 1 ] , i   ∣   p a t h [ x ] [ j ] 即 表 示 加 上 这 条 新 的 抛 物 线 后 的 状 态 , f [ i   ∣   p a t h [ x ] [ j ] ] = m i n ( f [ i   ∣   p a t h [ x ] [ j ] ] , f [ i ] + 1 ) 。 即 更 新 新 状 态 的 最 小 值 。 ②、DP求f数组,对于每一个状态i,先任选一个该状态还未覆盖到的点x,\\\qquad接着枚举所有覆盖x点的抛物线所能覆盖的状态path[x][j],j∈[0,n-1],\\\qquad i\ |\ path[x][j]即表示加上这条新的抛物线后的状态,f[i\ |\ path[x][j]]=min(f[i\ |\ path[x][j]],f[i]+1)。\\\qquad即更新新状态的最小值。 DPfixx线path[x][j],j[0,n1]i  path[x][j]线f[i  path[x][j]]=min(f[i  path[x][j]],f[i]+1)

注意: 函 数 a b s ( i n t   x ) , 是 求 整 数 绝 对 值 的 。    而 函 数 f a b s ( d o u b l e   x ) , 是 求 小 数 绝 对 值 的 。 函数abs(int \ x),是求整数绝对值的。\\\ \ \qquad而函数fabs(double\ x),是求小数绝对值的。 abs(int x)  fabs(double x)

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

#define P pair<double,double>
#define x first
#define y second

using namespace std;

const int N=20,M=1<<18;
double eps=1e-8;

int n,m,T;
int path[N][N];
int f[M];
P v[N];

int cmp(double a,double b)
{
    if(fabs(a-b)<eps) return 0;
    if(a<b) return -1;
    return 1;
}

int main()
{
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++) cin>>v[i].x>>v[i].y;
        
        memset(path,0,sizeof path);
        for(int i=0;i<n;i++)
        {
            path[i][i]=1<<i;
            for(int j=0;j<n;j++)
            {
                double x1=v[i].x,y1=v[i].y;
                double x2=v[j].x,y2=v[j].y;
                if(!cmp(x1,x2)) continue;
                double a=(y1/x1-y2/x2)/(x1-x2);
                double b=y1/x1-a*x1;
                if(cmp(a,0)>=0) continue;
                
                int State=0;
                for(int k=0;k<n;k++)
                {
                    double x=v[k].x,y=v[k].y;
                    if(!cmp(a*x*x+b*x,y))
                        State+=1<<k;
                }
                
                path[i][j]=State;
            }
        }

        memset(f,0x3f,sizeof f);
        f[0]=0;
        for(int i=0;i+1<1<<n;i++)
        {
            int x=0;
            for(int j=0;j<n;j++)
                if(!(i>>j&1))
                {
                    x=j;
                    break;
                }
                
            for(int j=0;j<n;j++)
                f[i|path[x][j]]=min(f[i|path[x][j]],f[i]+1);
        }
        
        cout<<f[(1<<n)-1]<<endl;
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值