NOIP 2016 提高组 Day2 愤怒的小鸟

本文介绍了一种使用状态压缩动态规划(状压DP)的方法来解决类似于小鸟射击游戏的问题。具体而言,针对给定的数据范围(N<=18),文章详细阐述了如何通过枚举状态并利用抛物线特性确定最少射击次数。文中提供了算法原理、关键步骤及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/u011056504/article/details/53367538


原理我懂了

注意到数据范围:N<=18 
有什么算法? 
暴力?状压! 
状压DP,对于每只猪1和0表示是否被打掉了 
设f[s]为当前状态的最小步数 
我们知道,三个点可以确定一个抛物线 
已知一个点是原点,那么再来两个点就可以确定一个抛物线,设点i和点j确定的抛物线表示为(i,j) 
每次枚举一个状态s,再枚举两只猪i,j,当然i,j不在s里面 
那么设所有经过(i,j)这条抛物线的点的集合为s’ 
那么f[ss]=f[s]+1,f[0]=1 
那么现在还需要预处理的就是每个集合s’ 
用a[i,j]存放抛物线(i,j)所经过的点的集合 
那么转移方程变成了 

f[sa[i,j](i,js)]=minf[sa[i,j](i,js)], f[s]+1),f[0]=1

时间复杂度O(2n+n2)


临时代码,还要慢慢写。

网上的代码。为什么我的思路跟他一样,但通过不呢??

#include <cmath>
#include <cstdio>
#include <cstring>
#define MAXN 20
using namespace std;
const double eps=1e-7;
struct Point
{
    double x,y;
    __attribute__((__optimize__("-O2"))) Point(){}
    __attribute__((__optimize__("-O2"))) Point(double x,double y):x(x),y(y){}
};
Point p[MAXN],res;
int n,m,T;
int dp[1<<MAXN];
int status[MAXN][MAXN];
double A,B,a,b;
__attribute__((__optimize__("-O2"))) inline double sqr(double x){return x*x;}
__attribute__((__optimize__("-O2"))) inline bool equal(double a,double b){return fabs(a-b)<eps;}
__attribute__((__optimize__("-O2"))) inline int mymin(int a,int b){return a<b?a:b;}
__attribute__((__optimize__("-O2"))) inline Point Solve(double a,double b,double c,double d,double e,double f)
{
    return Point((c*e-b*f)/(a*e-b*d),(a*f-c*d)/(a*e-b*d));
}
__attribute__((__optimize__("-O2"))) inline void MathProblem()
{
    memset(status,0,sizeof status);
    for (int i=1;i<=n;i++)
    {
        status[i][i]=1<<i-1;
        for (int j=i+1;j<=n;j++)
        {
            if (p[i].x==p[j].x&&p[i].y!=p[j].y) continue;
            res=Solve(p[i].x*p[i].x,p[i].x,p[i].y,p[j].x*p[j].x,p[j].x,p[j].y);
            A=res.x;B=res.y;
            if (equal(A,0.0)||A>=0.0) continue;
            status[i][j]=(1<<i-1)|(1<<j-1);
            for (int k=1;k<=n;k++)
                if (k!=i&&k!=j)
                {
                    if (p[i].x==p[k].x&&p[i].y!=p[k].y) continue;
                    res=Solve(p[i].x*p[i].x,p[i].x,p[i].y,p[k].x*p[k].x,p[k].x,p[k].y);
                    a=res.x;b=res.y;
                    if (equal(A,a)&&equal(B,b)) status[i][j]|=(1<<k-1);
                }
            status[j][i]=status[i][j];
        }
    }
    return ;
}
__attribute__((__optimize__("-O2"))) inline int Dynamic_Programming()
{
    memset(dp,0x7f,sizeof dp);dp[0]=0;
    for (int i=0;i<(1<<n)-1;i++)
        for (int j=1;j<=n;j++)
            for (int k=1;k<=n;k++)
                dp[i|status[j][k]]=mymin(dp[i|status[j][k]],dp[i]+1);
    return dp[(1<<n)-1];
}
__attribute__((__optimize__("-O2"))) int main()
{
    //freopen("angrybirds.in","r",stdin);freopen("angrybirds.out","w",stdout);
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
        MathProblem();
        printf("%d\n",Dynamic_Programming());
    }
    return 0;
}

我的代码 ,调试的都要吐血了。没有通过

#include <bits/stdc++.h>
//#define debug 1
using namespace std;
const int MM=18;
struct Point{
    double x;
    double  y;
};

vector<Point> p;
int dp[1<<MM];
int same[MM][MM];
int T,n,m;

bool check(double a, double b, Point pp){
    if( abs(  pp.y- a*pp.x*pp.x - b*pp.x  )<=1e-9 ) return  true;
        else  return false;
}


void math(){
    memset( same,0 ,sizeof(same) );
    for(int i=0;i<n;i++){
        for(int j=i+1; (j<n);j++){
            if(same[i][j]!=0) continue;
            //cout <<" i, j"<<i<<" "<<j;
            //int s=0;
            if(  ( p[i].x-p[j].x)==0.0  ) continue;
            double 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));
            double b =  ( p[i].y - a * p[i].x * p[i].x ) / p[i].x;

            #ifdef debug
                cout << "a , b :" <<a <<"  " <<  b <<endl;;
            #endif // debug

            if(a>0.0 || a<-1e-9) continue;
            //还没有考虑到 两个点只能确定开口向上的抛物线


            int s = 1<<i | 1<<j;
            for(int k=0;(k<n) ;k++)
                if( (k!=i) && (k!=j) && check(a,b,p[k] ) ) {
                     s = s | (1<<k);
                }
            same[i][j] = s;
           // cout << i << " "<<j <<"  "<<bitset<10>(same[i][j])<<endl;
            for(int dd=0; dd<n ;dd++){
                for(int ff=0;ff<n ;ff++){
                        if( (s>>dd&1) &&  (s>>ff & 1) && dd!=ff) {
                              same[dd][ff] = s;
                              same[ff][dd] =s;
                        }
                }
            }
            dp[s]=1;
        }
      }
      
      for(int i=0;i<n;i++){
         same[i][i] = 1<<i;         
      }

}

   

 #ifdef debug
      for(int dd=0;dd<n ;dd++){
                for(int ff=0;ff<n ;ff++){
                   cout << " i  j  same[i][j] " << dd<<" "<<ff<<" ::"<<bitset<10>( same[dd][ff]) << " ";
                }
                cout << endl;
            }
#endif // debug



int dprogram(){          
      dp[0]=0;
      for(int i=0;i<n;i++) dp[ 1<<i ] =1;    
      for(int i=0;i< (1<<n);i++) dp[i]=100000000;
      for(int s=0;s< 1<<n; s++){
            for(int i=0;i<n;i++){
                if( !( s>>i & 1) )   dp[ s | 1<<i ] =  min( dp[ s | 1<<i ], dp[s] + 1 );
                for(int j=i+1;j<n;j++){
                    if( !( s>>i & 1) &&  !( s>>j & 1)  ){
                        dp[ s | same[i][j] ] =  min( dp[ s | same[i][j] ], dp[s] + 1 );
                        //cout << bitset<10>(s) <<" " <<i <<" "<< j <<" " << bitset<10>( s| same[i][j] )<<"  "<< dp[s| same[i][j]] << endl;
                    }
                }
            }
        }

       return dp[(1<<n)-1] ;
}


int main(){

    cin >> T;
    while(T--){
        cin >> n>>m;
        p.clear();
        for(int i=0;i<n;i++){
            double a,b; cin>>a>>b;
            p.push_back((Point){a,b});
        }
        math();
        cout << dporgram();
   }
   
   return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值