COCI2016/2017 Round1T6 Vjestica

文章目录

题目

题目1
题目2
题目3

分析

既然每个单词的字母可以随意调换位置,那么字母的位置就无所谓了,一个单词仅由各个字母的数量决定。发现 n ≤ 16 n\leq 16 n16,考虑状压DP。

集合 S S S表示当前这颗树上选了哪些单词, d p [ S ] dp[S] dp[S]表示最优字典树的结点个数, d p [ S ] dp[S] dp[S]可以由每一个 d p [ A ] dp[A] dp[A] A ⊂ S A\subset S AS)和 d p [ ∁ S A ] dp\left[\complement_SA\right] dp[SA]转移,即合并这两棵字典树。

考虑集合 A A A中每个单词中各个字母出现的最少次数 m i ( 0 ≤ i ≤ 25 ) m_i(0\leq i\leq25) mi(0i25),则这棵树大概这个样子的:
图1
但是可能每个分支间还能合并,可参见样例一,所以根据这一点DP即可。
(这是个人理解,如与读者有出入,或无法理解,评论区欢迎您)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 16
#define INF 0x3f3f3f3f
int N;
int cnt[MAXN+5][30];
int dp[(1<<MAXN)+5],Min[30];

int main(){
	freopen("vjestica.in" ,"r", stdin);
	freopen("vjestica.out","w",stdout);
	scanf("%d",&N);
	getchar();
	for(int i=1;i<=N;i++){
		char c=getchar();
		while(c!='\n'){
			cnt[i][c-'a']++;
			dp[1<<(i-1)]++;
			c=getchar();
		}
	}
	for(int S=3;S<=(1<<N)-1;S++){
		memset(Min,0x3f,sizeof Min);
		for(int i=0;i<N;i++)
			if((S>>i)&1)
				for(int j=0;j<26;j++)
					Min[j]=min(Min[j],cnt[i+1][j]);//找到m
		int tmp=0;
		for(int i=0;i<26;i++)
			tmp+=Min[i];//统计有多少可以合并
		dp[S]=min(dp[S]?dp[S]:INF,INF);
		for(int i=1;i<S;i++){
			if((i|S)==S){
				int j=S^i;
				dp[S]=min(dp[S],dp[i]+dp[j]-tmp);
				//由上图可知,要合并导致减少的结点和i、j都无关
			}
		}
	}
	printf("%d",dp[(1<<N)-1]+1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值