zoukankan      html  css  js  c++  java
  • HUST 1605 Gene recombination

    隐式图搜索

    训练的题目,题意:输入n表示串(串为基因,只会出现ACGT)的长度,下面两行长度为n的串,第一个为起始串,第二个为目标串。

    对串能做两种操作。1.将头元素移动到尾部。2.最前面两个元素交换位置。从起始串到目标串的最少操作次数是多少,输出

    这题一看,觉得是DP,后来发了两三分钟的样子想到了是搜索。对于当前的串,它是一个状态,通过两个操作,能产生两个新的状态,所以这个过程就可以建图,搜索,找出两点间的最短路。注意这里不是树,因为很容易想到,这个图是可以有环的。另外,可以大致计算到状态数是很多的(串长最大为12),所以不能显式建图,当然也没必要显式建图,因为很多点(状态)是不会去到的

    很快打出代码,过sample,提交,超时。超时还是预料到的,因为没有做剪枝,会对相同状态搜索

    然后就想怎么剪枝,显然是要记录那些状态被搜索过,这个是最初想到的(但为什么不写呢,反省)

    注意这里的状态时一个字符串,并不好表示它,所以可以想到转化为数字,状态压缩!因为只有ACGT,所以对应为0123,那么整个串就是一个四进制数。

    状态数最大可以对应到4^12的,但是悲剧的是,这题会卡内存,不能用vis数组来标记,另外,这个图,并不是全部状态都会去到。

    那些怎么办呢?用vector?把搜索的状态存在vector中,每得到一个状态,就去vector里面扫描一次看看是否存在?这样依然是超时的。那么可用set?直接在set中查找这个状态?后来我没往这里想了,因为从最初我就想到了哈希(但是为什么不写呢?反省!),哈希应该是最快的。

    最后就静下来写哈希,写哈希怎么写呢?状态是一个字符串,它可以变为一个四进制数,两者是完全等价的,那么是用这个四进制数去哈希好呢,还是用字符串去哈希好呢?最后选择了用字符串去哈希,使用BKDHash去做,冲突少效果好

    为了再快点,在哈希的时候,是用字符串去计算映射地址的,但是地址里保存是的那个四进制数!

    (另外:处理冲突,使用了静态链表,即数组模拟)

    到了这里,就没什么困难了,就是搜索了,搜索时,起始状态入队,次数为0,以后每次搜索,次数加1即可

    具体步骤:

    1.得到一个新的字符串,在哈希表中查找它,如果哈希表中已经有了,就跳过,证明这个状态已经被搜索过,不要再搜它,就算搜它,也不是最优解,因为搜索时是按照步数从小到大来的。当前步数一定比之前的大

    2.如果在哈希表中没有这个状态,那么插入哈希表中,并且这个状态要入队。

    3.所以队列的元素应该记录两个信息,状态,搜到这个状态需要多少次。

    3.可以知道一个状态不会两次入队,一个状态一旦入队,它的次数就是最小次数

    另外,网上有人说,这个宽度搜索需要优先队列,这是多余的,不需要,使用优先队列的目的是为了每次出队的元素都是次数最少的,但是整个搜索的过程就是从次数递增的规则来的,所以后来入队的元素的次数一定 >= 之前的元素的次数,况且,一个状态不会二次入队

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define N 3000000 
    //这东西,最少要4,5百万,3百万都不行,会越界,存不下所有状态
    //数组并不是开小点或者开大点好,因为这个数组是对应哈希的,数组的大小
    //会多少影响到哈希表的冲突,测试了一下,1千万到3千万间时间较好
    #define LEN 15
    #define INF 0x3f3f3f3f
    #define MM 0x7fffffff
    
    
    int n;
    char S[LEN] , T[LEN];
    int st,ed;
    struct State{ //压缩成数字来表示一个状态
       int s,c;
    };
    int head[N+10] , tot; //哈希
    struct edge{
       int s,next;
    }e[N+10];
    
    typedef struct edge edge;
    typedef struct State State;
    queue<State>q;
    
    void add(int index ,int s)
    {
       e[tot].s=s;
       e[tot].next=head[index];
       head[index]=tot++;
    }
    
    int cton(char *str) //字符串转数字
    {
       int c=1 , ans=0;
       for(int i=n-1; i>=0; i--)
       {
          int s;
          if(str[i]=='A')      s=0;
          else if(str[i]=='C') s=1;
          else if(str[i]=='G') s=2;
          else                 s=3;
          ans += s*c;
          c *= 4;
       }
       return ans;
    }
    
    void ntoc(int x , char *str) //数字转字符串
    {
       for(int i=n-1; i>=0; i--)
       {
          int s=x%4;
          if(s==0)       str[i] = 'A';
          else if(s==1)  str[i] = 'C';
          else if(s==2)  str[i] = 'G';
          else           str[i] = 'T';
    
          x /= 4;
       }
       str[n] = '\0';
    }
    
    int BKDHash(char *str) //字符串的哈希映射用BKD
    {
       int len = strlen(str);
       int seed = 131;
       int res = 0;
       for(int i=0; i<len; i++)
          res = res*seed + str[i];
       res = (res&MM)%N;
       return res;
    }
    
    int find(char *str , int s) //查找
    {
       int index = BKDHash(str);
       for(int k=head[index]; k!=-1; k=e[k].next)
         if(e[k].s == s)
            return k;
       add(index,s); 
       //在哈希表中查不到这个状态,是一个新的状态,添加进哈希表中
       return -1;
    }
    
    void BFS()
    {
       int res;
       int index;
       int i,j;
       char s1[LEN] , s2[LEN];
       State sta;
       while(!q.empty())
       {
          State u=q.front()  ,  v;
          ntoc(u.s,s1); q.pop();
    
          //第一种转换方式,头元素放到尾部
          for(i=0,j=1; j<n; i++,j++) s2[i] = s1[j];
          s2[n-1] = s1[0]; s2[n] = '\0';
          v.s=cton(s2);
    
          index = find(s2,v.s);
          if(index == -1) //一个新的状态
          {
             v.c = u.c+1;
             q.push(v);
             if(v.s == ed)
             { res=v.c; break; }
          }
    
          //第二种转换方式,两个头元素交换
          for(int i=2; i<n; i++)  s2[i] = s1[i];
          s2[0] = s1[1];  s2[1] = s1[0];  s2[n]='\0';
          v.s=cton(s2);
    
          index = find(s2,v.s);
          if(index == -1)
          {
             v.c=u.c+1;
             q.push(v);
             if(v.s == ed)
             { res=v.c; break; }
          }
       }
       printf("%d\n",res);
    }
    
    int main()
    {
        //freopen("fuckcase.txt","r",stdin);
        //freopen("output.txt","w",stdout);
       while(scanf("%d",&n)!=EOF && n)
       {
          scanf("%s%s",S,T);
          if(!strcmp(S,T))
          { printf("0\n"); continue; }
    
          tot=0;
          memset(head,-1,sizeof(head));
          st=cton(S); ed=cton(T);
          int index = BKDHash(S); //字符串的哈希用BKD
          add(index,st);
          State sta;
          sta.s=st; sta.c=0;
          while(!q.empty()) q.pop(); 
          //忘记清空队列,wa了很多次,后来发现单case输入和多case输入不同,才发现
          //引以为戒
          q.push(sta);
          BFS();
       }
       return 0;
    }
  • 相关阅读:
    改动文件名称
    十五周 项目1 工资数据的输入
    通过YAJL获取json中的值
    图数据库之Pregel
    hdu 1028 Ignatius and the Princess III
    iOS使用ffmpeg播放rstp实时监控视频数据流
    Android的Bitmap和BitmapDrawable类解析-android学习之旅(六十)
    MAC中在eclipse luna上搭建移动平台自己主动化測试框架(UIAutomator/Appium/Robotium/MonkeyRunner)关键点记录
    QCustomPlot使用手冊(三)
    Mac下搭建react native开发环境
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3046033.html
Copyright © 2011-2022 走看看