bzoj2747 [2012中山市选]捡金子(gold) 树形dp

40 篇文章 0 订阅

Description


从前有一个迷宫,迷宫的外形就像一棵带根树,每个结点(除了叶子结点外)恰好有K个儿子。
一开始你在根结点,根结点的K个儿子分别标记为‘A’, ‘B’, ‘C’….,而结点‘A’的K个儿子结点分别标记为‘AA’,‘AB’,‘AC’……,依此类推。这棵树一共有L层。
现在你事先知道M个结点中有金子,并且你可以派出N个机器人去收集金子。首先你可以分别指定每一个机器人的目标结点,于是这些机器人就会收集从根结点到其目标结点这条路径上(包括目标结点)所有的金子,但是每个位置的金子只能被收集一次。

现在你需要制定一个目标的分配方案,使得收集到的金子最多。

对于20%的数据有1<=M<=20.
对于40%的数据有1<=M<=2000.
对100%的数据,有1<=M<=50000,1<=K<=26,1<=L<=50,1<=N<=50

Solution


考场上有思路但是做不出来的题,要注意这种情况了
果然是数据结构题做多了变傻了,满脑子的骚操作却没有正确打开方式。想要建trie却发现爆内存,想要提叶子结点却发现做不出来,尴尬

容易想到根据包含关系暴力判断建树,那么每个节点价值都为1。考虑树形dp,设 f[i][j] 为节点i为根的子树用j个机器人的最大收益, f[i][j]=max(f[k][l]+f[i][jl]) ,记得要算上本身的权值

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
const int N=50005;
const int E=50005;
struct edge{int y,next;}e[E];
struct P{char s[51];}pos[N];
int f[N][51];
int ls[N],edCnt=0,cnt=0;
int present,m,n,root=0;
void addEdge(int x,int y) {
    e[++edCnt]=(edge){y,ls[x]}; ls[x]=edCnt;
}
bool cmp1(P a,P b) {return strlen(a.s)<strlen(b.s);}
bool pd(char *a,char *b) {
    if (strlen(a)>strlen(b)) return 0;
    rep(i,0,strlen(a)-1) if (a[i]!=b[i]) return 0;
    return 1;
}
void insert(int x) {
    int now=root;
    while (233) {
        bool flag=false;
        for (int i=ls[now];i;i=e[i].next) {
            if (!pd(pos[e[i].y].s,pos[x].s)) continue;
            now=e[i].y;
            flag=true;
        }
        if (!flag) {
            addEdge(now,x);
            return ;
        }
    }
}
void dp(int now) {
    for (int i=ls[now];i;i=e[i].next) {
        dp(e[i].y);
        drp(j,n,1) rep(k,1,j) {
            f[now][j]=std:: max(f[now][j],f[e[i].y][k]+f[now][j-k]);
        }
    }
    rep(i,1,n) f[now][i]+=1;
}
int main(void) {
    freopen("data.in","r",stdin);
    int kk,l; scanf("%d%d%d%d",&m,&kk,&l,&n);
    rep(i,1,m) scanf("%s",pos[i].s);
    std:: sort(pos+1,pos+m+1,cmp1);
    rep(i,1,m) {
        present=i;
        insert(i);
    }
    dp(root);
    printf("%d\n", f[root][n]-1);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值