NOIP2016 [憤怒的小鳥] 狀壓DP

題目描述

Kiana最近沉迷於一款神奇的遊戲無法自拔。

簡單來說,這款遊戲是在一個平面上進行的。

有一架彈弓位於(0,0)處,每次Kiana可以用它向第一象限發射一隻紅色的小鳥,小鳥們的飛行軌跡均爲形如y=ax2+bxy=ax^2+bxy=ax2+bx的曲線,其中a,b是Kiana指定的參數,且必須滿足a<0。

當小鳥落回地面(即x軸)時,它就會瞬間消失。

在遊戲的某個關卡里,平面的第一象限中有n只綠色的小豬,其中第i只小豬所在的座標爲(xi,yi)。

如果某隻小鳥的飛行軌跡經過了(xi,yi),那麼第i只小豬就會被消滅掉,同時小鳥將會沿着原先的軌跡繼續飛行;

如果一隻小鳥的飛行軌跡沒有經過(xi,yi),那麼這隻小鳥飛行的全過程就不會對第i只小豬產生任何影響。

例如,若兩隻小豬分別位於(1,3)和(3,3),Kiana可以選擇發射一隻飛行軌跡爲y=−x2+4xy=-x^2+4xy=x2+4x的小鳥,這樣兩隻小豬就會被這隻小鳥一起消滅。

而這個遊戲的目的,就是通過發射小鳥消滅所有的小豬。

這款神奇遊戲的每個關卡對Kiana來說都很難,所以Kiana還輸入了一些神祕的指令,使得自己能更輕鬆地完成這個遊戲。這些指令將在【輸入格式】中詳述。

假設這款遊戲一共有T個關卡,現在Kiana想知道,對於每一個關卡,至少需要發射多少隻小鳥才能消滅所有的小豬。由於她不會算,所以希望由你告訴她。

輸入輸出格式

輸入格式:

第一行包含一個正整數T,表示遊戲的關卡總數。

下面依次輸入這T個關卡的信息。每個關卡第一行包含兩個非負整數n,m,分別表示該關卡中的小豬數量和Kiana輸入的神祕指令類型。接下來的n行中,第i行包含兩個正實數(xi,yi),表示第i只小豬座標爲(xi,yi)。數據保證同一個關卡中不存在兩隻座標完全相同的小豬。

如果m=0,表示Kiana輸入了一個沒有任何作用的指令。

如果m=1,則這個關卡將會滿足:至多用⌈n3+1⌉\left \lceil \frac{n}{3} + 1 \right \rceil3n+1只小鳥即可消滅所有小豬。

如果m=2,則這個關卡將會滿足:一定存在一種最優解,其中有一隻小鳥消滅了至少⌊n3⌋\left \lfloor \frac{n}{3} \right \rfloor3n只小豬。

保證1<=n<=18,0<=m<=2,0<xi,yi<10,輸入中的實數均保留到小數點後兩位。

上文中,符號⌈x⌉\left \lceil x \right \rceilx⌊x⌋\left \lfloor x \right \rfloorx分別表示對c向上取整和向下取整

輸出格式:

對每個關卡依次輸出一行答案。

輸出的每一行包含一個正整數,表示相應的關卡中,消滅所有小豬最少需要的小鳥數量



解題報告:

枚舉每種狀態,轉移

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps = 1e-10 ;
const int S = ( 1 << 19 ) ;
struct Fx{
    double x , y;
}a [19];

int dp [S] , sta [S] ;
int idc = 0 ;

double ga ( int i , int j ) {
    return ( a [i].y * a [j].x - a [i].x * a [j].y ) / ( ( a [i].x * a [j].x ) * ( a [i].x - a [j].x ) );
}

double gb ( int i , int j ) {
    return ( a [i].x * a [i].x * a [j].y - a [j].x * a [j].x * a[i].y ) / ( ( a [i].x * a [j].x ) * ( a [i].x - a [j].x ) );
}

void init ( int n ){
    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = i ; j <= n ; ++ j ) {
            ++idc ;
            if ( i^j ){
                    double t=a[i].x/a[j].x,t2=a[j].x*a[i].x-a[i].x*a[i].x,t3=a[j].y*t-a[i].y;
                double a1=t3/t2;
                if (a1>-eps) continue;
                double b1=-(a1*a[i].x*a[i].x-a[i].y)/a[i].x;
            //    double a1 = ga ( i , j ) ;
            //    double b1 = gb ( i , j ) ;
                for ( int k = 1 ; k <= n ; ++ k )
                    if ( a1 * a [k].x * a [k].x + b1 * a [k].x > a [k].y - eps && a1 * a [k].x * a [k].x + b1 * a [k].x < a [k].y + eps) 
                        sta [idc] |= (1 << (k-1) ) ;
            } else sta [idc] = 1 << ( i-1 ) ;
            
        }
}

int main () {
    int T ;
    scanf ( "%d" , &T ) ;
    while ( T -- ) {
        idc = 0 ;
        memset ( sta , 0 , sizeof ( sta ) );
        int n , pos;
        scanf ( "%d%d" , &n , &pos ) ;
        for ( int i = 1 ; i <= n ; ++ i ) scanf ( "%lf%lf" , & a[i].x , & a[i].y ) ;
        init ( n ) ;
        memset ( dp , 0x3f3f3f3f , sizeof ( dp ) );
        dp [0] = 0 ;
        for ( int i = 0 ; i < ( 1 << n ) ; ++ i ) 
            for ( int j = 1 ; j <= idc ; ++ j )
                if ( ( i | sta [j] ) != i ) dp [ i | sta [j] ] = min ( dp [ i | sta [j] ] , dp [i] + 1 ) ;
        printf ( "%d\n" , dp [ (1 << n) - 1 ] ) ;
    }
    return 0 ;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章