CF1082F Speed Dial
CF1082F Speed Dial
题面:洛谷
解析:
写了一个上午,终于写完了因为博主实在太菜了。这道题我是看着动态规划的标签进来做的。可以发现对于每个号码串,按的快捷键一定对应的是它的前缀,观察到\(k\)很小,我开始有一个暴力的想法,在状态中把每一个已经确定的前缀记录下来,这样就可以做转移。后来想到用\(Trie\)树可以简洁的记录前缀,而且因为\(Trie\)树的特性,我们无需把所有的前缀记录下来,只需要记录最近的前缀(因为这样一定是最优秀的)。那么有状态\(f(i,j,k)\)表示当前在\(i\)节点,
上一个最近的前缀对应的是节点\(j\)(容易看出\(j\)一定在\(u\)到\(root\)的链上),有\(k\)个前缀尚未使用,转移枚举\(k\)的分配即可,注意重叠字符串的转移。
代码
#include<cstdio>
#include<cstring>
#define N 505
using namespace std;
const int INF=1e9;
int n,K,val[N],f[N][N][15];
char str[N][15];
#define gc() getchar()
inline int In(){
char c=gc(); int x=0,ft=1;
for(;c<'0'||c>'9';c=gc()) if(c=='-') ft=-1;
for(;c>='0'&&c<='9';c=gc()) x=x*10+c-'0';
return x*ft;
}
inline int min(int a,int b){
return a<b?a:b;
}
int ch[N][10],d[N],sz[N],sum[N],leaf[N],rt,tot;
inline void insert(int p){
int len=strlen(str[p]),u=rt;
sz[rt]+=val[p]; sum[rt]=d[rt]*val[rt];
for(int i=0;i<len;++i){
if(!ch[u][str[p][i]-'0']){
ch[u][str[p][i]-'0']=++tot;
d[tot]=d[u]+1;
}
u=ch[u][str[p][i]-'0'];
sz[u]+=val[p];
}
sum[u]+=d[u]*val[p]; leaf[u]+=val[p];
}
void dfs(int u){
for(int i=0;i<10;++i) if(ch[u][i]) dfs(ch[u][i]),sum[u]+=sum[ch[u][i]];
}
int dp(int u,int las,int j){
if(f[u][las][j]!=INF) return f[u][las][j];
if(!j) return sum[u]-d[las]*sz[u];
int t1[N],t2[N];
for(int i=0;i<j;++i) t1[i]=0;
for(int i=0,v;i<10;++i){
v=ch[u][i]; if(!v) continue;
for(int t=0;t<j;++t) t2[t]=t1[t],t1[t]=INF;
for(int t=0;t<j;++t)
for(int k=0;k<j-t;++k)
t1[k+t]=min(t1[k+t],dp(v,u,t)+t2[k]);
}
f[u][las][j]=min(f[u][las][j],t1[j-1]);
// -> min(\sum_{v=u.son} f(v,u,k)) \sum k=j-1
for(int i=0;i<=j;++i) t1[i]=leaf[u]*(d[u]-d[las]);
for(int i=0,v;i<10;++i){
v=ch[u][i]; if(!v) continue;
for(int t=0;t<=j;++t) t2[t]=t1[t],t1[t]=INF;
for(int t=0;t<=j;++t)
for(int k=0;k<=j-t;++k)
t1[k+t]=min(t1[k+t],dp(v,las,t)+t2[k]);
}
f[u][las][j]=min(f[u][las][j],t1[j]);
// -> min\sum_{v=u.son} f(v,u,k)) \sum k=j
return f[u][las][j];
}
int main(){
n=In(); K=In(); rt=tot=1; d[rt]=0;
for(int i=1;i<=n;++i){
scanf("%s",str[i]); val[i]=In();
insert(i);
}
for(int i=0;i<=tot;++i)
for(int j=0;j<=tot;++j)
for(int k=0;k<=K;++k)
f[i][j][k]=INF;
dfs(rt);
printf("%d\n",dp(rt,0,K));
return 0;
}