題解 P6873 [COCI2013-2014#6] FONT
阿新 • • 發佈:2021-06-27
實話說,別想太多,就能速切本題。
演算法分析:記憶化搜尋
第一眼看到可能會想到狀壓 dp,但事實上沒那麼複雜。如果要狀壓的話,狀態數有 \(N\times 2^{26}=6710886400\) 種,顯然在時空上均無法接受。
考慮記憶化搜尋。簡單地,用 map 記錄狀態。由於順序無關,每一輪搜尋可以從上一輪之後開始。為了記憶化簡單,我們可以把已經包含字元的狀態壓縮在 \(sta\) 裡,然後用 \(f_{fro,sta}\) 表示到 \(fro\) 時,使用狀態為 \(sta\) 的情況總數。然後搜尋即可。
下面說明記憶化搜尋比狀壓 dp 優秀。
狀壓 dp 雖然常數較小,但所有狀態幾乎是跑滿的。由於僅有 \(26\)
在最壞情況下,每一層只能獲得一個新字元。由於單詞長度較長(不超過 \(100\)),因此這種情況很難出現。如果出現,可以加入特判剔除這種狀況(然鵝本題中並沒有這樣噁心的資料)。
需要注意的是,在 map 中進行查詢時,最好不要直接將狀態作為下標進行查詢。因為在 map 的機制裡,若找不到目標,將自動建立一個空值。而 map 的效率與其元素個數直接相關。這樣做將大幅降低 map 的效率,且空間上也不能承受。
下面給出程式碼:
#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; }
歡迎討論交流,請點個贊哦~