作法:在 Trie 上DP,dp[ x ][ i ] 代表在 x 的子樹裡選 i 個單字的最小打字數(打的字都在這棵子樹裡),在DP前先把樹拆點轉成二元樹,這樣就可以 O(k) 轉移,不過聽說在每個結點都作一次背包問題也可以過,這樣變O(kk)轉移。
(把樹轉成二元樹的作法在 IOI2005 Rivers (HOJ 326) 也有用到,前幾天剛好寫到那題...)
code :
#include<bits/stdc++.h> using namespace std; int id(char c) { return c-'a' ; } int ch[20001][26],val[20001],ccnt ; void insert(char *t) { int now=0 , len=strlen(t) ; for(int i=0;i<len;i++) { int u=id(t[i]) ; if(!ch[now][u]) { ccnt++ ; memset(ch[ccnt],0,sizeof(ch[ccnt])) ; ch[now][u]=ccnt ; } now=ch[now][u] ; } val[now]=1 ; } vector<int> v[100001] ; int w[200001] ; char s[20001] ; int dp[100001][101] ; int DP(int x,int num) { if(num==0) return 0 ; if(num==1) return 1 ; /// ~~~~~~~~~~~ if(v[x].empty()) { return 20001 ; //if(val[x]) return num==1 ? w[x] : 20001 ; //else return 20001 ; } if(dp[x][num]!=-1) return dp[x][num] ; int &ans=dp[x][num] ; ans=20001 ; if((int)v[x].size()==1) { if(num==1) return (ans=1) ; ans=min(ans,num*w[x]+DP(v[x][0],num)) ; if(val[x]) ans=min(ans,num*w[x]+DP(v[x][0],num-1)) ; return ans ; } ans=min(ans,num*w[x]+DP(v[x][0],num)) ; ans=min(ans,num*w[x]+DP(v[x][1],num)) ; for(int i=1;i<num;i++) ans=min(ans,num*w[x]+DP(v[x][0],i)+DP(v[x][1],num-i)) ; if(!val[x]) return ans ; ans=min(ans,num*w[x]+DP(v[x][0],num-1)) ; ans=min(ans,num*w[x]+DP(v[x][1],num-1)) ; for(int i=1;i<num-1;i++) ans=min(ans,num*w[x]+DP(v[x][0],i)+DP(v[x][1],num-i-1)) ; return ans ; } main() { freopen("C.txt","r",stdin) ; freopen("Cout.txt","w",stdout) ; int T ; scanf("%d",&T) ; int tc=0 ; while(T--) { ccnt=0 ; memset(ch[0],0,sizeof(ch[0])) ; memset(val,0,sizeof(val)) ; memset(w,0,sizeof(w)) ; for(int i=0;i<=100000;i++) v[i].clear() ; int n,k ; scanf("%d%d",&n,&k) ; while(n--) { scanf("%s",s) ; insert(s) ; } for(int i=1;i<=ccnt;i++) w[i]=1 ; for(int i=0;i<=ccnt;i++) for(int j=0;j<26;j++) if(ch[i][j]) v[i].push_back(ch[i][j]) ; for(int i=0;i<=ccnt;i++) { int sz=v[i].size() ; if(sz<=2) { if(val[i]) w[i]=1 ; continue ; } int tmp=ccnt+1 ; for(int j=1;j<sz-2;j++) { ccnt++ ; v[ccnt].push_back(v[i][j]) ; v[ccnt].push_back(ccnt+1) ; } ccnt++ ; v[ccnt].push_back(v[i][sz-2]) ; v[ccnt].push_back(v[i][sz-1]) ; v[i][1]=tmp ; v[i].resize(2) ; } memset(dp,-1,sizeof(dp)) ; printf("Case #%d: %d\n",++tc,DP(0,k)) ; } }
沒有留言:
張貼留言