zoukankan      html  css  js  c++  java
  • HDU 3341 Lost's revenge(AC自动机+状压DP)

    Lost's revenge

    Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
    Total Submission(s): 4548    Accepted Submission(s): 1274


    Problem Description
    Lost and AekdyCoin are friends. They always play "number game"(A boring game based on number theory) together. We all know that AekdyCoin is the man called "nuclear weapon of FZU,descendant of Jingrun", because of his talent in the field of number theory. So Lost had never won the game. He was so ashamed and angry, but he didn't know how to improve his level of number theory.

    One noon, when Lost was lying on the bed, the Spring Brother poster on the wall(Lost is a believer of Spring Brother) said hello to him! Spring Brother said, "I'm Spring Brother, and I saw AekdyCoin shames you again and again. I can't bear my believers were being bullied. Now, I give you a chance to rearrange your gene sequences to defeat AekdyCoin!".

    It's soooo crazy and unbelievable to rearrange the gene sequences, but Lost has no choice. He knows some genes called "number theory gene" will affect one "level of number theory". And two of the same kind of gene in different position in the gene sequences will affect two "level of number theory", even though they overlap each other. There is nothing but revenge in his mind. So he needs you help to calculate the most "level of number theory" after rearrangement.
     
    Input
    There are less than 30 testcases.
    For each testcase, first line is number of "number theory gene"
     
    N(1<=N<=50). N=0 denotes the end of the input file.
    Next N lines means the "number theory gene", and the length of every "number theory gene" is no more than 10.
    The last line is Lost's gene sequences, its length is also less or equal 40.
    All genes and gene sequences are only contains capital letter ACGT.
     
    Output
    For each testcase, output the case number(start with 1) and the most "level of number theory" with format like the sample output.
     
    Sample Input
    3 AC CG GT CGAT 1 AA AAA 0
     
    Sample Output
    Case 1: 3 Case 2: 2
     
    分析:通过题意,位置可以重置,也就是最后的字符串的AGCT数量和原字符串数量相同.
    由题,我们可以想到用AC自动机来记录这些“数论基因”
    直观的看的话,用DP来构造,就有41*41*41*41*500种状态,很明显这样是超出内存的
    但实际上A+G+C+T<=40 很多状况是不存在的,所以我们就可以进行状态压缩
    设 num0=A的数量;num1=G;num2=C;num3=T
    这样总共的状态就有(num0+1)*(num1+1)*(num2+1)*(num3+1)*500种,在数量最大的情况下也就是
    11*11*11*11*500;
    根据进制的特点,我们在列举不重复的情况的时候,可以这样把当前的状态这样化为十进制数
      S=A*(num1+1)*(num2+1)*(num3+1)+G*(num2+1)*(num3+1)+C*(num3+1)+T*1;
    dp[S][j]也就是当前的状态,j是在自动机上的节点位置
     
    代码如下:
    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    #include <queue>
    using namespace std;
    #define INF 0x3f3f3f3f
    int num[4];
    int bit[4];
    int dp[11*11*11*11+10][550];
    int maxx,end_len;
    struct Trie
    {
        int Next[550][4];//4是这里讨论4个小写字母的情况,根据情况修改
        int fail[550],end[550];//end数组表示以该节点结尾的字符串的数量
        int root,L;//L用来标记节点序号,以广度优先展开的字典树的序号
        int newnode()  //建立新节点
        {
            for(int i = 0;i < 4;i++)
                Next[L][i] = -1;     //将该节点的后继节点域初始化
            end[L++] = 0;
            return L-1;    //返回当前节点编号
        }
        void init() //初始化操作
        {
            L = 0;
            root = newnode();
        }
        int get_id(char ch)
        {
            if(ch=='A')return 0;
            else if(ch=='G')return 1;
            else if(ch=='C')return 2;
            else if(ch=='T')return 3;
        }
        void insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for(int i = 0;i < len;i++)
            {
                if(Next[now][get_id(buf[i])] == -1)  //如果未建立当前的后继节点,建立新的节点
                    Next[now][get_id(buf[i])] = newnode();
                now = Next[now][get_id(buf[i])];
            }
            end[now]++;//以该节点结尾的字符串数量增加1
        }
        void build()
        {
            queue<int>Q; //用广度优先的方式,将树层层展开
            fail[root] = root;
            for(int i = 0;i < 4;i++)
                if(Next[root][i] == -1)
                    Next[root][i] = root;
                else
                {
                    fail[Next[root][i]] = root;
                    Q.push(Next[root][i]);
                }
            while( !Q.empty() )
            {
                int now = Q.front();
                Q.pop();
                end[now]+=end[fail[now]];
                for(int i = 0;i < 4;i++)
                    if(Next[now][i] == -1)
                        Next[now][i] = Next[fail[now]][i];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配
                    else
                    {
                        fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点
                        Q.push(Next[now][i]);
                    }
            }
        }
        void solve()
        {
            maxx=0;
          int maxlen=bit[0]*(num[0]+1);
          for(int i=0;i<=maxlen+1;i++)
            for(int j=0;j<L;j++)
            dp[i][j]=INF;
            dp[0][0]=0;
            for(int A=0;A<=num[0];A++)
              for(int G=0;G<=num[1];G++)
               for(int C=0;C<=num[2];C++)
                 for(int T=0;T<=num[3];T++)
            {
                int s=A*bit[0]+G*bit[1]+C*bit[2]+T*bit[3];
                  for(int j=0;j<L;j++)
                  {
                     if(dp[s][j]<INF-5)
                     {
                        for(int k=0;k<4;k++)
                        {
                          if(k==0&&A==num[0])continue;
                         else if(k==1&&G==num[1])continue;
                         else if(k==2&&C==num[2])continue;
                         else if(k==3&&T==num[3])continue;
                         if(dp[s+bit[k]][Next[j][k]]==INF)
                         dp[s+bit[k]][Next[j][k]]=dp[s][j]+end[Next[j][k]];
                         else
                         dp[s+bit[k]][Next[j][k]]=max(dp[s+bit[k]][Next[j][k]],dp[s][j]+end[Next[j][k]]);
                        }
                     }
                  }
            }
          int  max_len=num[0]*bit[0]+num[1]*bit[1]+num[2]*bit[2]+num[3]*bit[3];
            for(int i=0;i<L;i++){
                if(dp[max_len][i]<INF-5)
            maxx=max(dp[max_len][i],maxx);
            }
            cout<<maxx<<endl;
        }
    };
    Trie ac;
    char buf[110];
    int main()
    {
        int  t,n,ans,len,Case=0;
        while(scanf("%d",&n)!=EOF)
        {
            Case++;
            if(n==0)break;
            memset(num,0,sizeof(num));
          ac.init();
          while(n--)
         {
          scanf("%s",buf);
          ac.insert(buf);
         }
         ac.build();
         scanf("%s",buf);
         len=strlen(buf);
         for(int i=0;i<len;i++)
         {
             if(buf[i]=='A')
             num[0]++;
             else if(buf[i]=='G')
             num[1]++;
             else if(buf[i]=='C')
             num[2]++;
             else if(buf[i]=='T')
             num[3]++;
         }
         bit[0]=(num[1]+1)*(num[2]+1)*(num[3]+1);
         bit[1]=(num[2]+1)*(num[3]+1);
         bit[2]=(num[3]+1);
         bit[3]=1;
         printf("Case %d: ",Case);
         ac.solve();
        }
        return 0;
    }
     
     
  • 相关阅读:
    Timestamp 转 date
    【mysql】 mysql 子查询、联合查询、模糊查询、排序、聚合函数、分组----------语法
    【mysql】新增、修改、删除、查询 语法讲义
    【jQuery05】通过按键 来切换 class
    【jQuery04】折叠树
    【jQuery03】简单的选项卡切换
    【jQuery02】点击标题面板显示内容
    python-day70--django-Mysql-单表增删改查
    python-day68--模型层基础(model)
    python-day67--MTV之Template
  • 原文地址:https://www.cnblogs.com/a249189046/p/7603302.html
Copyright © 2011-2022 走看看