• 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]);
        // -> minsum_{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
    ",dp(rt,0,K));
        return 0;
    }
    
    
    
  • 相关阅读:
    linux awk命令详解
    Linux 大页面使用与实现简介(转)
    二层设备与三层设备的区别--总结
    Windows下的cd命令
    linux常用命令
    上班第一天
    linux 内核移植和根文件系统的制作
    Sizeof与Strlen的区别与联系
    嵌入式软件工程师面试题
    SpringBoot简单打包部署(附工程)
  • 原文地址:https://www.cnblogs.com/pkh68/p/10533798.html
Copyright © 2020-2023  润新知