【題解】LuoGu2831/noip2016:憤怒的小鳥
阿新 • • 發佈:2018-12-11
原題傳送門 【題解】 我先想到了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.