1. 程式人生 > >【題解】LuoGu1941/noip2014:飛揚的小鳥

【題解】LuoGu1941/noip2014:飛揚的小鳥

原題傳送門

這道題是一道比較明顯的dp dp[i][j]dp[i][j]表示橫座標為i、高度為j的最少點選數

發現,同一時刻可以點選多次螢幕、不點選則下降 可以理解為:向上做完全揹包,向下做01揹包

邊界也很好處理,若點選一次上升高度為X 則向上飛的話最多飛到m+X,把高出m的都算作m的 另外,障礙物所在處是無法通過的,需要賦一個很大的值

最終的輸出分兩種情況,很好處理,不講了

Code:

uses math;
var
    dp:array[0..11000,0..2000] of longint;
    l,h,x,y,cnt:array[0..100000] of longint;
    flag:boolean;
    n,m,k,i,j,p:longint;
    ans:int64;

begin
    readln(n,m,k);
    for i := 1 to n do
    begin
        readln(x[i],y[i]);
        h[i] := m + 1;
    end;
    for i := 1 to k do
    begin
        read(p);
        cnt[p] := 1;//打標記
        readln(l[p], h[p]);
    end;
    for i := 1 to n do
        for j := 1 to n + m do dp[i][j] := maxlongint >> 1;
    for i := 1 to m do dp[0][i] := 0;//初始化
    for i := 1 to n do
    begin
        for j := x[i] + 1 to m + x[i] do     //完全揹包
            dp[i][j] := min(dp[i - 1][j - x[i]] + 1, dp[i][j - x[i]] + 1);
        for j := m + 1 to m + x[i] do dp[i][m] := min(dp[i][m], dp[i][j]);
        for j := 1 to m - y[i] do       //01揹包
            dp[i][j] := min(dp[i - 1][j + y[i]], dp[i][j]);
        for j := 1 to l[i] do dp[i][j] := maxlongint >> 1;
        for j := h[i] to m do dp[i][j] := maxlongint >> 1;
    end;
    ans := maxlongint;
    for i := 1 to m do ans := min(ans, dp[n][i]);
    if ans < (maxlongint >> 1) then
    begin
        writeln(1); writeln(ans);
    end else
    begin
        writeln(0);
        for i := n downto 1 do
        begin
            flag := false;
            for j := 1 to m do
                if dp[i][j] < (maxlongint >> 1) then
                begin
                    flag := true;
                    break;
                end;
            if flag then
            begin
                ans := 0;
                for j := 1 to i do inc(ans, cnt[j]);
                writeln(ans);
                break;
            end;
        end;
    end;
end.