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

77 篇文章 0 订阅
37 篇文章 0 订阅

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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值