• HDU 2296 Ring ( Trie图 && DP && DP状态记录)


    题意 : 给出 m 个单词,每一个单词有一个权重,如果一个字符串包含了这些单词,那么意味着这个字符串拥有了其权重,问你构成长度为 n 且权重最大的字符串是什么 ( 若有权重相同的,则输出最短且字典序最小的 )

    分析 : 如果你做过 POJ 2778 或者 HDU 2243 以及诸如此类的题目,那么这道题的难点就不在构建 Trie图上了,没有接触过Trie图的建议先了解,下面进入正题。这道题相对于普通的 AC自动机orTrie图 + DP 的题目而言,共同点是都是利用 Trie图进行状态的转移,现在增加了权重以及要求输出具体的字符串答案。我们定义 DP[i][j] 为构建了长度为 i 且最后一个字符为 j 的字符串最大权重,由于每一个状态都对应一个字符串,所以再构建一个三维字符数组 s[i][j][k] 表示当前 i、j 状态下具体的字符串为 s[i][j][0~k-1],那么状态转移方程就是

    DP[i+1][ Trie[j][k] ] = max( DP[i+1][ Trie[j][k] ] , DP[i][j] + Trie[j][k].val )

    ( Trie[j][k] 代表 j 状态可以一步转移到 k状态,如果你做过类似题目,那你不会陌生)

    在状态转移的时候需要时时更新 s[i][j][k] 这个三维数组,当取得更优值的时候需要更新,最后只要在DP的过程当中记录最优的权重、状态i、j下标然后DP结束后输出即可。当然有个小优化,这种DP属于向前的DP,如果当前DP值是你设置的初值,那么它是没意义的,可以直接continue,因为它不会对后面的DP值产生影响。

    #include<string.h>
    #include<stdio.h>
    #include<queue>
    using namespace std;
    const int Max_Tot = 1200;
    const int Letter = 26;
    
    int dp[55][1200];
    char s[55][1200][55];///存储每一个状态所代表的具体字符串
    
    struct Aho{
        struct StateTable{
            int Next[Letter];
            int fail, val;
        }Node[Max_Tot];
        int Size;
        queue<int> que;
    
        inline void init(){
            while(!que.empty()) que.pop();
            memset(Node[0].Next, 0, sizeof(Node[0].Next));
            Node[0].fail = Node[0].val = 0;
            Size = 1;
        }
    
        inline void insert(char *s, int val){
            int now = 0;
            for(int i=0; s[i]; i++){
                int idx = s[i] - 'a';
                if(!Node[now].Next[idx]){
                    memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                    Node[Size].fail = Node[Size].val = 0;
                    Node[now].Next[idx] = Size++;
                }
                now = Node[now].Next[idx];
            }
            Node[now].val = val;
        }
    
        inline void BuildFail(){
            Node[0].fail = 0;
            for(int i=0; i<Letter; i++){
                if(Node[0].Next[i]){
                    Node[Node[0].Next[i]].fail = 0;
                    que.push(Node[0].Next[i]);
                }else Node[0].Next[i] = 0;
            }
            while(!que.empty()){
                int top = que.front(); que.pop();
                Node[top].val += Node[Node[top].fail].val;///这里需要注意!
                for(int i=0; i<Letter; i++){
                    int &v = Node[top].Next[i];
                    if(v){
                        que.push(v);
                        Node[v].fail = Node[Node[top].fail].Next[i];
                    }else v = Node[Node[top].fail].Next[i];
                }
            }
        }
    }ac;
    char tmp[111][55];
    int main(void)
    {
        int nCase;
        scanf("%d", &nCase);
        while(nCase--){
            int n, m;
            scanf("%d %d", &n, &m);
            for(int i=0; i<m; i++)
                scanf("%s", tmp[i]);
            int tmpVal;
            ac.init();
            for(int i=0; i<m; i++){
                scanf("%d", &tmpVal);
                ac.insert(tmp[i], tmpVal);
            }
            ac.BuildFail();
    
            for(int i=0; i<=n; i++){///将所有DP的值赋为 -1
                for(int j=0; j<ac.Size; j++){
                    dp[i][j] = -1;
                    s[i][j][0] = '';
                }
            }
    
            dp[0][0] = 0;///定义初始状态
    
            char str[60];
            int ii, jj, MaxSum;
            ii = jj = MaxSum = 0;
            for(int i=0; i<n; i++){
                for(int j=0; j<ac.Size; j++){
                    if(dp[i][j] >= 0){///如果当前dp值不是初始状态则进入if,否则其dp值毫无意义,直接跳过
                        for(int k=25; k>=0; k--){///一开始我是想谋求字典序最小而从后往前,但是WA一发后我发现我错了,实际上顺序不重要
                            int newi = i+1;
                            int newj = ac.Node[j].Next[k];
                            int sum = dp[i][j] + ac.Node[ newj ].val;
                            if(sum > dp[newi][newj]){
                                dp[newi][newj] = sum;
                                strcpy(s[newi][newj], s[i][j]);
                                int len = strlen(s[i][j]);
                                s[newi][newj][len] = k+'a';
                                s[newi][newj][len+1] = '';
                            }else if(sum == dp[newi][newj]){///谋求字典序最小应该实在dp值相等情况下
                                strcpy(str, s[i][j]);
                                int len = strlen(str);
                                str[len] = 'a'+k;
                                str[len+1] = '';
                                if(strcmp(str, s[newi][newj]) < 0)
                                    strcpy(s[newi][newj], str);
                            }
    
                            if(dp[newi][newj] >= MaxSum){///更新一下最终的答案
                                if(dp[newi][newj] == MaxSum){
                                    int L1 = strlen(s[newi][newj]);
                                    int L2 = strlen(s[ii][jj]);
                                    if(L1<=L2 && strcmp(s[newi][newj], s[ii][jj])<0)
                                        ii = newi, jj = newj;
                                }else{
                                    MaxSum = dp[newi][newj];
                                    ii = newi, jj = newj;
                                }
                            }
    
                        }
                    }
                }
            }
    
            if(MaxSum <= 0) puts("");///如果最后权值依旧是 0 那么输出空串
            else puts(s[ii][jj]);
        }
        return 0;
    }
  • 相关阅读:
    win10安装mxnet cuda9.0-gpu版
    win10下安装pytorch,torchvision
    python:Non-ASCII character ‘xe2′ in file
    win10与子系统ubuntu之间互访文件
    Spyder更改默认工作路径已经文件路径
    tensorflow读取jpg格式图片报错 ValueError: Only know how to handle extensions: ['png']; with Pillow installed matplotlib can handle more images
    Anaconda中spyder 安装tensorflow
    深度学习---预训练
    机器学习(深度学习)为什么需要训练,训练出来的模型具体又是什么?
    中本聪比特币论文
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/7635861.html
Copyright © 2020-2023  润新知