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;
}


posted @ 2019-03-14 21:54  pkh68  阅读(181)  评论(0编辑  收藏  举报