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.