1. 程式人生 > >【題解】LuoGu2831/noip2016:憤怒的小鳥

【題解】LuoGu2831/noip2016:憤怒的小鳥

原題傳送門 【題解】 我先想到了dfs,估計了一下時間複雜度,估不出來,覺得懸,就“瞟了一眼”題解,發現第一篇就是dfs,然後信心滿滿的碼好,莫名wa掉,找了很長時間錯也沒找出來。

然後我又想起一年前某位老師講狀壓dp的時候講過這道題,就改用了狀壓dp

dp[i]表示豬的狀態為i時,用掉的鳥的最小值。 對於i,比如i=1001,那麼表示第1、4只豬死掉了 預處理:num[i][j]表示過i,j兩隻豬的拋物線能殺死豬的個數

狀態轉移方程(用pascal寫法了): dp[0] = 0 //邊界 dp[i or num[j][k]] = min(dp[i] + 1) //多一條拋物線 dp[i or (1 << (j - 1))] = min(dp[i] + 1) //多一隻豬

發現,第三個狀態轉移方程沒有用,完全可以由第二條得到 那麼說明本題的狀態可以完全由拋物線推到,我們又可以加一個小優化:若j豬以屬於i狀態,那麼就不用做了

Code:

uses math;
var
    x,y:array[0..1000] of double;
    x1,x2,y1,y2,a,b:double;
    power:array[0..1000] of int64;
    dp:array[0..2000000] of int64;
    num:array[0..100,0..100] of longint;
    qnum,p,i,j,k,n,m:longint;

function check(x,y:double):boolean;//判斷浮點數是否相等
var
    tmp:double;

begin
    if x > y then
    begin
        tmp := x; x := y; y := tmp;
    end;
    exit(y - x <= 0.00000001);
end;

begin
    readln(qnum);
    power[0] := 1;
    for i := 1 to 20 do power[i] := power[i-1] << 1;
    for p := 1 to qnum do
    begin
        readln(n,m);
        for i := 1 to n do readln(x[i],y[i]);
        fillchar(num,sizeof(num),0);
        for i := 1 to n do//預處理num
            for j := i + 1 to n do
            begin
                if x[i] = x[j] then continue;
                x1 := x[i]; y1 := y[i];
                x2 := x[j]; y2 := y[j];
                a := (y1 * x2 - y2 * x1) / (x1 * x1 * x2 - x2 * x2 * x1);
                b := (y1 - a * x1 * x1) / x1;//解方程
                if a < 0 then
                begin
                    for k := 1 to n do
                        if check(a * x[k] * x[k] + b * x[k], y[k]) then
                            num[i][j] := num[i][j] or power[k-1];
                end;
            end;
        fillchar(dp,sizeof(dp),$7f); dp[0] := 0;
        for i := 0 to power[n] - 1 do
            for j := 1 to n do
                if power[j-1] and i = 0 then//小優化
                begin
                    dp[i or power[j-1]] := min(dp[i or power[j-1]], dp[i] + 1);
                    for k := j + 1 to n do
                        if x[j] <> x[k] then
                            dp[i or num[j][k]] := min(dp[i or num[j][k]], dp[i] + 1);
                end;
        writeln(dp[power[n] - 1]);
    end;
end.