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.