题解 P6873 [COCI2013-2014#6] FONT
实话说,别想太多,就能速切本题。
算法分析:记忆化搜索
第一眼看到可能会想到状压 dp,但事实上没那么复杂。如果要状压的话,状态数有 \(N\times 2^{26}=6710886400\) 种,显然在时空上均无法接受。
考虑记忆化搜索。简单地,用 map 记录状态。由于顺序无关,每一轮搜索可以从上一轮之后开始。为了记忆化简单,我们可以把已经包含字符的状态压缩在 \(sta\) 里,然后用 \(f_{fro,sta}\) 表示到 \(fro\) 时,使用状态为 \(sta\) 的情况总数。然后搜索即可。
下面说明记忆化搜索比状压 dp 优秀。
状压 dp 虽然常数较小,但所有状态几乎是跑满的。由于仅有 \(26\) 个字符,且一个单词很可能包含多种字符,因此在大多数情况下,很快就能组合成一个测试句且情况很容易重复。在已经成为测试句的情况下,剩余的单词选与不选均不会产生影响。记这时已经使用到第 \(fro\) 个单词,那么可以直接返回 \(2^{n-fro}\) 作为答案。
在最坏情况下,每一层只能获得一个新字符。由于单词长度较长(不超过 \(100\)),因此这种情况很难出现。如果出现,可以加入特判剔除这种状况(然鹅本题中并没有这样恶心的数据)。
需要注意的是,在 map 中进行查找时,最好不要直接将状态作为下标进行查找。因为在 map 的机制里,若找不到目标,将自动建立一个空值。而 map 的效率与其元素个数直接相关。这样做将大幅降低 map 的效率,且空间上也不能承受。
code:
#include<bits/stdc++.h>
#define reg register
#define ll long long
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline int read();
const int N=105,M=30,all=(1<<26)-1;
int b[N],n;
char c[N];
ll ans,pww[M] {1};
map<int,ll>f[M];
ll dfs(int fro,int sta){
if(sta==all)return pww[n-fro];
//直接计算得出
if(f[fro].find(sta)!=f[fro].end())return f[fro][sta];
//用 map.find 不会建立空值
ll ans=0;
F(i,fro+1,n)ans+=dfs(i,sta|b[i]);
f[fro][sta]=ans;//记忆化
return ans;
}
int main() {
F(i,1,26)pww[i]=pww[i-1]<<1ll;
n=read();
F(i,1,n) {
scanf("%s",c+1);
int len=strlen(c+1);
F(j,1,len)b[i]|=(1<<(c[j]-'a'));//状态压缩
}
ans=dfs(0,0);
printf("%lld",ans);
return 0;
}
inline int read() {
reg int x=0;
reg char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
欢迎讨论交流,请点个赞哦~
本文来自博客园,作者:Maplisky,转载请注明原文链接:https://www.cnblogs.com/lbh2021/p/14940663.html

浙公网安备 33010602011771号