zoukankan      html  css  js  c++  java
  • [noip2016]愤怒的小鸟<状压dp+暴搜>

    题目链接:https://vijos.org/p/2008

    现在回过头去看去年的考试题,发现都不是太难,至少每道题都有头绪了。。。

    这道题的数据范围是18,这么小,直接暴力呗,跑个暴搜就完了,时间也就O(n^3)

    【思路】

    先枚举任意两个的抛物线,这个位置需要O(n^2),接着针对每一个抛物线看可以经过多少点,暴力跑一个,时间复杂度O(n^3),不过这一步可以在枚举抛物线时做。。

    接着是用一个数组mark[i][j]记录经过点i,j的抛物线可以穿过哪些点。。这个位置,我们就可以用状态压缩解决,一个二进制,第i位为1表示可以穿过这个点,0则不行

    然后就是f数组,f[i]的i状态下需要多少个鸟去打,i的二进制下表示当前有哪些点被穿过了,然后i的范围在二进制下就是0到11111111(n个1)

    然后就是最后一个大循环,首先判断能不能达到状态i,如果可以就枚举第j的点,如果没有被穿过就是f[i|state[j]]=min(f[i|state[j]],f[i]+1)

    (要么是原来的状态,要么从当前状态发射一只鸟)

    接着就是枚举从j穿过的抛物线类型,f[i|mark[j][k]]=min(f[i|mark[j][k]],f[i]+1)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<queue>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<cstdlib>
     8 #define maxn 30
     9 #define LL long long
    10 using namespace std;
    11 
    12 double x[maxn],y[maxn];
    13 double ex=1e-8;
    14 int mark[maxn][maxn];
    15 int f[(1<<18)+5],state[maxn];
    16 int n,m;
    17 
    18 void work(){
    19     scanf("%d%d",&n,&m);
    20     for(int i=0;i<n;i++){
    21         scanf("%lf%lf",&x[i],&y[i]);
    22     } 
    23     memset(mark,0,sizeof(mark));
    24     for(int i=0;i<n;i++){
    25         for(int j=i+1;j<n;j++){
    26             double x1=x[i],x2=x[j],y1=y[i],y2=y[j];
    27             double a=(x1*y2-y1*x2)/(x1*x2*x2-x1*x1*x2);
    28             if(a>=0)continue;//抛物线,开口向下
    29             double b=(y1-a*x1*x1)/x1;
    30             for(int k=0;k < n;k++){
    31                 if(abs(y[k]-x[k]*x[k]*a-x[k]*b)<=ex){
    32                 //误差范围内,近似为0
    33                     //if(!(mark[i][j]&state[k]))mark[i][j]|=state[k];
    34                     mark[i][j]+=state[k];
    35                 } 
    36             }     
    37         }
    38     }
    39     memset(f,127,sizeof(f));
    40     int inf=f[1];f[0]=0;
    41     int lim=(1<<n)-1;//最终状态是11111111111
    42     
    43      for(int i=0;i<=lim;i++){//枚举每种状态
    44          if(f[i]!=inf){
    45              for(int j=0;j<n;j++){
    46                  if(!(i&state[j])){//当前状态没有j 
    47                      int now=i|state[j];
    48                      f[now]=min(f[i]+1,f[now]);
    49                      //可以从f[i]再射出一只 
    50                     for(int k=j+1;k<n;k++){
    51                         int noww=i|mark[j][k];//j,k抛物线的所有猪 
    52                         f[noww]=min(f[i]+1,f[noww]);
    53                     }
    54                  }
    55              }
    56          } 
    57          
    58      }
    59     printf("%d
    ",f[lim]);
    60 }
    61 
    62 int main(){
    63     int T;
    64     scanf("%d",&T);
    65     state[0]=1;
    66     for(int i=1;i<=20;i++)
    67      state[i]=state[i-1]<<1;
    68     while(T--){
    69         work();
    70     }return 0;
    71 }
    View Code

    【总结】

    当题中的问题状态可以转换成0,1表示的时候,可以考虑用状压dp,不过要正确使用二进制

  • 相关阅读:
    C# webBrowser禁止在新窗口打开,强制在本窗口打开
    C# StatusStrip状态栏 例程
    iphone实现弹出框视图
    在iOS开发中使用FMDB
    XCode 4.2 新功能 Storyboard
    FMDB一个sqlite的封装
    Xcode 4的新特性
    sqlite加密SQLCipher
    跨iPhone应用程序的方便,模块化的代码共享:静态库和跨项目的引用
    Navicat for SQLite For Ma OS X
  • 原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7724241.html
Copyright © 2011-2022 走看看