[排序][二分][dp]JZOJ 2747 撿金子

Description

從前有一個迷宮,迷宮的外形就像一棵帶根樹,每個結點(除了葉子結點外)恰好有K個兒子。
一開始你在根結點,根結點的K個兒子分別標記爲‘A’, ‘B’, ‘C’….,而結點‘A’的K個兒子結點分別標記爲‘AA’,‘AB’,‘AC’……,依此類推。這棵樹一共有L層。
現在你事先知道M個結點中有金子,並且你可以派出N個機器人去收集金子。首先你可以分別指定每一個機器人的目標結點,於是這些機器人就會收集從根結點到其目標結點這條路徑上(包括目標結點)所有的金子,但是每個位置的金子只能被收集一次。
現在你需要制定一個目標的分配方案,使得收集到的金子最多。

題解

首先,我們將m個結點從小到大排序
那麼我們要把樹造出來,其實只用將這幾個用金子的結點連起來就好了
我們找到2~m的父親(或爺爺或曾爺爺或曾曾爺爺或曾曾曾爺爺.....)(用!!二分!!實現),存入隊列裏邊
最後就可以dp了
    我們設f[i][j]爲以第i個結點爲根,用第j個機器人的收集金子的最大數
    狀態轉移方程就是 f[x][i]=max(f[x][i],f[x][i-j]+f[y][j])
    (Ps: x爲當前結點 i爲當前枚舉到用第i個機器人 y爲他下一輩的結點 j爲他下一輩結點用的機器人)

代碼

uses math;
type strx=string[55];
var     num,m,k,l,n,x,i,j:longint;
        next,last,first,bz:array[0..50050]of longint;
        a:array[0..50050]of strx;
        f:array[0..50050,0..50]of longint;
        w:string;

procedure insert(i,j:longint);
begin
        inc(num);
        next[num]:=last[i];
        last[i]:=num;
        first[num]:=j;
end;

procedure qsort(l,r:longint);
var     i,j:longint;
        mid,t:strx;
begin
        if (l>=r) then exit;
        i:=l; j:=r; mid:=a[(l+r) div 2];
        repeat
                while (a[i]<mid) do inc(i);
                while (a[j]>mid) do dec(j);
                if (i<=j) then
                begin
                        t:=a[i]; a[i]:=a[j]; a[j]:=t;
                        inc(i); dec(j);
                end;
        until i>j;
        qsort(l,j);
        qsort(i,r);
end;

function find(x:strx):longint;
var     l,r,mid:longint;
begin
        l:=1; r:=i;
        while (l<r) do
        begin
                mid:=(l+r)div 2;
                if (a[mid]<x)then l:=mid+1 else r:=mid;
        end;
        if (a[l]<>x) then exit(0) else exit(l);
end;

procedure dp(x:longint);
var     k,y,i,j:longint;
begin
        k:=last[x];
        while (k<>0) do
        begin
                y:=first[k];
                dp(y);
                for i:=n downto 1 do
                        for j:=1 to i do
                                f[x,i]:=max(f[x,i],f[y,j]+f[x,i-j]);
                k:=next[k];
        end;
        for i:=1 to n do f[x,i]:=f[x,i]+bz[x];
end;

begin
        readln(m,k,l,n);
        for i:=1 to m do readln(a[i]);
        inc(m);
        a[m]:='';
        qsort(1,m);
        x:=1;
        for i:=2 to m do
        begin
                w:=a[i];
                delete(w,length(w),1);
                j:=find(w);
                while (j=0) do
                begin
                        delete(w,length(w),1);
                        j:=find(w);
                end;
                insert(j,i);
                bz[i]:=1;
        end;
        dp(1);
        writeln(f[1,n]);
end.
發佈了263 篇原創文章 · 獲贊 423 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章