题解 Vjestica (状压DP)

原题

题面简化

有n个字符合集,求其前缀树的最少结点个数。

数据范围

n ≤ 16,1 ≤ m ≤1000000(字符个数)

思路

首先看到

n ≤ 16

可知,这道题是状压DP或者暴力

状态是什么?

因为是n的数据范围小,n为字符合集个数,所以状态是 n个字符合集的整体 的选择情况,
这里用S表示,二进制范围为 1~1111111111111111。

怎么进行转移?

因为目标值是前缀树的最少结点个数,
所以可以把这棵树拆为两颗进行合并,这也是树形DP的常见思路。

怎么拆?

因为这颗前缀树在节点数最少的条件下,一种结构就可能由不同状态转移而来,
所以,须枚举所有选择情况。

怎么合?

因为我们枚举了所有选择情况,其中一定有可转移为最优结构的选择,
所以我们直接把两棵树都连上根,把前缀树的公共前缀合并,
即 减去现在所有选择的字符合集的交集的字符个数。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,_[16][26],cnt[17][26],dp[1<<16];
char a[16][1000006];
int LCP(int s){
	int len=0;
	memset(_[n],0x3f,sizeof(_[n]));
	for(int i=0;i<n;i++)
	if(s&(1<<i)){
		for(int k=0;k<26;k++)
		_[n][k]=min(_[n][k],_[i][k]);
	}
	for(int k=0;k<26;k++)len+=_[n][k];
	return len;
}
int dfs(int s){
	int &ret=dp[s];
	if(ret!=-1)return ret;
	int lcp=LCP(s);
	if((s&(-s))==s)return ret=lcp;
	ret=100000000;
	for(int pS=(s-1)&s;pS>0;pS=(pS-1)&s)
		ret=min(ret,dfs(pS)+dfs(s^pS)-lcp);
	return ret;
}
int main(){
	memset(dp,-1,sizeof(dp));
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%s",a[i]);
		for(int j=0;a[i][j];j++){
			_[i][a[i][j]-'a']++;
		}
	}
	printf("%d",dfs((1<<n)-1)+1);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值