华容道有37,946,880个局面

image

上图大将4竖1横。五个长条中选k个横放,C(5,0)+...+C(5,5)=2**5 (尚未处理)。

盘4x5=20. 有两个空,它们的位置有C(20,2)=190种,这可不是局面的总数,因为没有考虑块的大小,移动时不能把曹老板驷马分尸。

分(图)层和分类。2x2x1曹, 2x1x1关, 1x2x4张等, 1x1x4卒。每类占据一层。

先穷举每类在它的图层上有多少种摆法,结果如下:

  • 2x2x1 12
  • 2x1x1 15
  • 1x2x4 21264
  • 1x1x4 116,280

然后搜索4层。把4层的bitmask or起来,如果有遮挡,就会有不止两个空(0),1的个数就不是18,用_mm_popcnt_u32数1的个数。

12*15*21264*116280/1e8≈4451亿,可裁剪掉很多:int popcnt[4] = { 4, 6, 14, 18 }; 符合条件才搜下一层。

g++ -msse4.2 -O3, Intel N100, 11.363秒。

容易改成12线程,每个线程处理一个曹老板(的位置)。像〔八皇后 〕一样可不用锁。

用法:

  • a.out 1 2 4 为1x2x4型生成数据124.dat
  • a.out 1 2  print layers[1][2]
  • a.out 搜索

分层避免了些重复计算?没想明白对称性问题。

迄今为止最快华容道! 0.09s!! 130行!!!

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <nmmintrin.h>
typedef unsigned uint;

FILE* open_file (int w, int h, int n, const char* mode) {
  char  fn[256];
  sprintf(fn, "%d%d%d.dat", w, h, n);
  return fopen(fn, mode);
}

struct LayerAry {
  uint* ary;
  int len;

  operator uint* () { return ary; }

  void load (int w, int h, int n) {
    FILE* fp = open_file(w, h, n, "rb");
    fseek(fp, 0, SEEK_END); ary = new uint[len = ftell(fp) / 4];
    fseek(fp, 0, SEEK_SET); fread(ary, 4, len, fp);
    fclose(fp);
    printf("%d%d%d %d\n", w, h, n, len);
  }

  ~LayerAry () { delete[] ary; }
};

struct GetLayer {
  uint b[5][4], w, h;
  FILE* fp;

  GetLayer(int ww, int hh, int n) : w(ww), h(hh) {
    fp = open_file(w, h, n, "wb");
    memset(b, 0, sizeof(b)); search(n);
    fclose(fp);
  }

  void print() {
    for (int y = 0; y < 5; y++) {
     for (int x = 0; x < 4; x++) putchar(b[y][x] ? '*' : '.');
     puts("");
    }
  }

  void save() {
    uint  bits = 0;
    for (int y = 0; y < 5; y++) {
     for (int x = 0; x < 4; x++)
      bits |= b[y][x] << (y * 4 + x);
    }
    fwrite(&bits, 4, 1, fp);
  }

  bool put(int x, int y) {
    if (x + w > 4 || y + h > 5) return false;
    for (int yy = y; yy < y + h; yy++) for (int xx = x; xx < x + w; xx++)
      if (b[yy][xx]) return false;
    for (int yy = y; yy < y + h; yy++) for (int xx = x; xx < x + w; xx++) b[yy][xx] = 1;
    return true;
  }

  void unput(int x, int y) {
    for (int yy = y; yy < y + h; yy++) for (int xx = x; xx < x + w; xx++) b[yy][xx] = 0;
  }

  void search (int n) {
    for (int y = 0; y < 5; y++) for (int x = 0; x < 4; x++) {
      if (!put(x, y)) continue;
      if (n == 1) save();
      else search(n - 1);
      unput(x, y);
    }
  }
};

void print (uint bits) {
  printf("%05x%3d\n", bits, _mm_popcnt_u32(bits));
  for (int y = 0; y < 5; y++) {
   for (int x = 0; x < 4; x++) putchar(bits & (1 << (y * 4 + x)) ? '*' : '.');
   puts("");
  }
}

LayerAry  las[4];
uint64_t  valid;

void search (int idx, uint bits) {
  static const int popcnt[4] = { 4, 6, 14, 18 };
  for (int i = 0; i < las[idx].len; i++) {
    uint b = bits | las[idx][i];
    if (_mm_popcnt_u32(b) != popcnt[idx]) continue;
    if (idx < 3) { search(idx + 1, b); continue; }
    if ((valid++ & 0xffffff) == 0) printf("%lld\n", valid);
  }
}

int main (int argc, char** argv) {
  if (argc == 4) {
    GetLayer gm(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
    return 0;
  }

  las[0].load(2,2,1); las[1].load(2,1,1); las[2].load(1,2,4); las[3].load(1,1,4);

  if (argc == 3) {
    print(las[atoi(argv[1])][atoi(argv[2])]);
    return 0;
  }

  search(0, 0); printf("\n\n%d\n", valid);
  return 0;
}
posted @ 2025-10-20 21:44  华容道专家  阅读(29)  评论(0)    收藏  举报