usaco 4.1 Cryptcowgraphy 剪枝

来源:互联网 发布:js老虎机效果 编辑:程序博客网 时间:2024/06/05 14:29

 这道题目需要大量优化,要用到ELFhash字符串哈希,空间不能开太大,否则会很慢,但是在我的机器上试,空间不影响速度,有可能是因为usaco服务器的问题
总结一下错误的地方
1)下标错误:把c,o,w搞混了
2)边界错误:对于字符数组,需要判断0<=i && i < n,一般在有+-的时候容易出错,更一般的,加法和乘法注意溢出,减法注意负数,除法注意除数为0的情况

 

优化措施总结如下

1)字符串的字符个数应该等于(目标字符串的长度)+3*k。如果不满足就可直接判断无解。
2)除了COW三个字符外,其他的字符的个数应该和目标串相一致。如果不一致也可直接判断无解。前两项都可以在搜索前进行 .
3)搜索中间肯定会出现很多字符串相同的情况,因此需要开一个hash来判断是否搜索过,用ELFhash可以对字符串很好的处理 ,这里哈希数组大小为131071
4)一个有解的字符串中,COW三个字母最早出现的应该是C,最后出现的应该是W,如果不满足则剪枝。
5)当前字符串被COW分为几个子串,每个子串一定也是目标串的子串,如果不符合可立即剪枝, 也可以用hash来存储。
6)需要优化搜索顺序。先枚举O,并且W要逆序枚举

经过上述优化,程序对于极端数据也可以在1s以内出解。

 

以下是我的代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <string>
  4. #include <map>
  5. using namespace std;
  6. /*
  7. PROG: cryptcow
  8. LANG: C++
  9. ID: heben991
  10. */
  11. const int N = 80, M = 256, D = 80, hash_size = 131071;
  12. // too large hash_size will be slow!!
  13. char code[N] = "Begin the Escape execution at the Break of Dawn";
  14. int len = strlen(code);
  15. int code_cnt[M], str_cnt[M];
  16. char s[D][N], ss[N], *p, ch, cow[5]="COW";
  17. bool hash[hash_size], code_hash[hash_size];
  18. int ELFhash(char *key)
  19. {
  20.     unsigned long h=0,g;
  21.     while(*key)
  22.     {
  23.         h=(h<<4) + *key++;
  24.         g=h & 0xF0000000L;
  25.         if(g) h^= g>>24;
  26.         h &= ~g;
  27.     }
  28.     return h % hash_size;
  29. }
  30. bool impossible(char *s, int n)
  31. {
  32.     int i=0, j, c, w;
  33.     for(c = 0; c < n && s[c]!='C'; ++c);
  34.     for(w = n-1; w >=0 && s[w]!='W'; --w);
  35.     if(c < n && w >= 0 && c >= w)return true;
  36.     strcpy(ss,s);
  37.     p = strtok(ss,cow);
  38.     while(p)
  39.     {
  40.         if(!code_hash[ELFhash(p)])return true;
  41.         p = strtok(0,cow);
  42.     }
  43.     return false;
  44. }
  45. bool ok(int deep, int n)
  46. {
  47.     int i, t, c, o, w;
  48.     if(strcmp(s[deep],code)==0)
  49.     {
  50.         return true;
  51.     }
  52.     if(n<=len)return false;
  53.     t = ELFhash(s[deep]);
  54.     if(!hash[t]) hash[t]=true;
  55.     else return false;
  56.     if(impossible(s[deep],n)) return false;
  57.     s[deep+1][n-3] = 0;
  58.     for(o = 1; o < n-1; ++o)
  59.     if(s[deep][o]=='O')
  60.     {
  61.         for(c = 0; c < o; ++c)
  62.         {
  63.             if(c>0) s[deep+1][c-1] = s[deep][c-1];
  64.             if(s[deep][c]=='C')
  65.             for(w = n-1; w > o; --w)
  66.             {
  67.                 if(w<n-1 && w-2>=0) s[deep+1][w-2] = s[deep][w+1];
  68.                 // hint: if(w<n-1 && w-2>=0)  w-2!!
  69.                 if(s[deep][w]=='W')
  70.                 {
  71.                     t = c;
  72.                     for(i = o+1; i < w; ++i) s[deep+1][t++] = s[deep][i];
  73.                     for(i = c+1; i < o; ++i) s[deep+1][t++] = s[deep][i];
  74.                     if( ok(deep+1,n-3) ) return true;
  75.                 }
  76.             }
  77.         }
  78.     }
  79.     return false;
  80. }
  81. bool all_in_code(char *s, int n)
  82. {
  83.     int i, j;
  84.     for(i = len-1; i >= 0; --i)
  85.     {
  86.         for(j = i+1; j <= len; ++j)
  87.         {
  88.             ch = code[j];
  89.             code[j] = 0;
  90.             code_hash[ELFhash(code+i)] = true;
  91.             code[j] = ch;
  92.         }
  93.     }
  94.     for(i = 0; i < n; ++i) str_cnt[s[i]]++;
  95.     for(i = 0; i < len; ++i) code_cnt[code[i]]++;
  96.     for(ch = 'A'; ch <= 'z'; ++ch)
  97.         if(ch!='C' && ch!='O' && ch!='W' && code_cnt[ch]!=str_cnt[ch])
  98.         {
  99.             printf("%c %d %d/n", ch, code_cnt[ch],str_cnt[ch]);
  100.             return 0;
  101.         }
  102.     return 1;
  103. }
  104. int main()
  105. {
  106.     int n;
  107.     freopen("cryptcow.in""r", stdin);
  108.     freopen("cryptcow.out","w",stdout);
  109.     gets(s[0]);
  110.     n = strlen(s[0]);
  111.     if( (n-len)%3==0 && all_in_code(s[0],n) && ok(0,n) )
  112.     {
  113.         int ans=0;
  114.         for(p=s[0];*p;++p) if(*p=='C')++ans;
  115.         printf("%d %d/n", 1,ans);
  116.     }
  117.     else puts("0 0");
  118.     return 0;
  119. }