捡金子

捡金子 ⁡ \operatorname{捡金子}

题目链接: luogu T145308 ⁡ \operatorname{luogu\ T145308} luogu T145308 / SSL比赛 1518 ⁡ \operatorname{SSL比赛\ 1518} SSL 1518

题目

从前有一个迷宫,迷宫的外形就像一棵带根树,每个结点(除了叶子结点外)恰好有 K K K 个儿子。

一开始你在根结点,根结点的 K K K 个儿子分别标记为 ‘A’, ‘B’, ‘C’…., 而结点 ‘A’ 的 K K K 个儿子结点分别标记为 ‘AA’,‘AB’,‘AC’…… ,依此类推。这棵树一共有 L L L 层。

现在你事先知道 M M M 个结点中有金子,并且你可以派出 N N N 个机器人去收集金子。首先你可以分别指定每一个机器人的目标结点,于是这些机器人就会收集从根结点到其目标结点这条路径上(包括目标结点)所有的金子,但是每个位置的金子只能被收集一次。

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

输入

输入的第一行有 4 4 4 个整数: M , K , L , N M,K,L,N M,K,L,N 。对应题目描述中的参数。

接下来 M M M 行,每行是一个字符串,表示所对应的结点上有金子。

输出

输出利用 N N N 个机器人所能捡到时最多几个结点上的金子。

样例输入1

5 3 3 1
ACC
ACB
AB
AC
A

样例输出1

3

样例输入2

5 3 3 2
ACC
ACB
AB
AC
A

样例输出2

4

数据范围

对于 20 % 20\% 20% 的数据,有 1 ≤ M ≤ 20 1\le M\le 20 1M20

对于 40 % 40\% 40% 的数据,有 1 ≤ M ≤ 2000 1\le M\le 2000 1M2000

对于 100 % 100\% 100% 的数据,有 1 ≤ M ≤ 50000 , 1 ≤ K ≤ 26 , 1 ≤ L ≤ 50 , 1 ≤ N ≤ 10 1\le M\le 50000, 1\le K\le 26, 1\le L\le 50, 1\le N\le 10 1M50000,1K26,1L50,1N10

思路

这道题其实十分暴力,只是数据结构要用 Trie 树,我比赛的时候用 map 暴力都拿了 50 50 50 分。

至于什么是 Trie 树,最近应该会找时间做做模板题,写个博客。
如果我记得,写完那个博客我应该会放个链接在这里,如果不记得,可以直接我博客搜。
不过肯定没人看QAQ

就有点贪心吧,就每次找到路上金子最多的一条链,然后拿走,就这样跑 n n n 次,就可以得出答案了。

代码

#include<cstdio>

using namespace std;

int m, k, l, n, a[5000001][26], what[5000001], le[5000001];
int fa[5000001], last, K = 1, have[5000001];
int an[5000001], maxn, maxx, ans;
bool get[5000001];
char c;

int getnum(int now) {
    
    //找到这一条链的金子数
	if (now == 1 || get[fa[now]]) return have[now];
	return have[now] + getnum(fa[now]);
}

void clear(int now) {
    
    //把这一条链的金子拿掉
	get[now] = 1;
	if (now == 1 || get[fa[now]]) return ;
	clear(fa[now]);
}

int main() {
    
    
	scanf("%d %d %d %d", &m, &k, &l, &n);//读入
	for (int i = 1; i <= m; i++) {
    
    
		last = 1;
		
		c = getchar();
		while (c < 'A' || c > 'Z') c = getchar();//建Trie数
		while (c >= 'A' && c <= 'Z') {
    
    
			if (a[last][c - 'A']) last = a[last][c - 'A'];
				else {
    
    
					fa[++K] = last;
					what[K] = c - 'A';
					a[last][what[K]] = K;
					last = K;
				}
			c = getchar();
		}
		
		have[last]++;
		le[i] = last;
	}
	
	for (int times = 1; times <= n; times++) {
    
    //每次删掉金子最多的链
		maxn = 0;
		for (int i = 1; i <= m; i++)
			if (!get[le[i]]) {
    
    
				an[i] = getnum(le[i]);
				if (an[i] > maxn) {
    
    
					maxn = an[i];
					maxx = i;
				}
			}
		clear(le[maxx]);
		ans += maxn;//记录这条链拿了多少金子
	}
	
	printf("%d", ans);//输出
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108210034
今日推荐