NOIP2016 愤怒的小鸟 状压DP

之前做完飞扬的小鸟的时候还以为这道题和它一样。。

题意:用最少的鸟把猪打完。
题解:看n的范围可知是状压DP。枚举每两只猪之间的抛物线,若抛物线符合题意就看这条抛物线还覆盖了哪些猪。而有些猪可能不会和任意一只猪在同一抛物线上,所以还要新增一条抛物线只覆盖一只猪。
状态转移方程: dp[i|para[j]]=min(dp[i|para[j]],dp[i]+1) d p [ i | p a r a [ j ] ] = m i n ( d p [ i | p a r a [ j ] ] , d p [ i ] + 1 )

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const double eps = 1e-8;
const int MAXN = 21;

struct Node{
    double x, y;
}P[MAXN];

int para[MAXN * MAXN], dp[(1 << 18) + 5];

inline void Solve(double x1, double y1, double x2, double y2, double &a, double &b){
    a = (y2 * x1 - y1 * x2) / (x1 * x2 * x2 - x1 * x1 * x2);
    b = (y2 * x1 * x1 - y1 * x2 * x2) / (x1 * x1 * x2 - x1 * x2 * x2);
}

inline bool check(double a, double b, double x, double y){ //检查第li条抛物线与第pi只猪是否相连 
    return fabs(a * x * x + b * x - y) < eps;
} 

int main(){
    freopen("in.txt", "r", stdin);
    int T; scanf("%d", &T);
    while(T--){
        int n, m, cnt = 0; scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
            scanf("%lf%lf", &P[i].x, &P[i].y);
        for(int i = 1; i <= n; i++){
            para[++cnt] = (1 << (i - 1)); //这条抛物线只经过一只小猪 
            for(int j = 1, vis = 0; j < i; j++){
                if(fabs(P[i].x - P[j].x) < eps) continue;
                if(vis & (1 << (j - 1))) continue; //之前已经有抛物线经过这只猪了 
                double a, b;

                Solve(P[i].x, P[i].y, P[j].x, P[j].y, a, b);
                if(a >= 0) continue; //不合法 
                para[++cnt] = (1 << (i - 1)); //这条抛物线起初只经过一只小猪 
                for(int k = 1; k < i; k++){
                    if(check(a, b, P[k].x, P[k].y)){
                        vis |= (1 << (k - 1));
                        para[cnt] |= (1 << (k - 1));
                    }
                }
            }
        }
        memset(dp, 0x3f, sizeof(dp)); dp[0] = 0;
        for(int i = 0; i < (1 << n); i++){
            for(int j = 1; j <= cnt; j++){
                dp[i | para[j]] = min(dp[i | para[j]], dp[i] + 1);
            }
        }
        printf("%d\n", dp[(1 << n) - 1]);
    }
    return 0;
}

循环的时候把起点设为0会更好写,但我不想改了233

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值