HDU 2553 N皇后问题 - dancing links

打表是最快的做法。退而求其次,直接搜就行了,注意下减枝即可。用DLX纯粹是为了套模板。

皇后问题的行列约束很容易转化成01矩阵的列,但对角约束却比较麻烦,因为可能不必完全满足:比如5*5的棋盘,有5行5列,这些要全部不重不漏的满足,但正对角线却有9条,而且只要不重就行了。反对角线亦然。

所以在每一步搜索的过程中,选择约束列的时候,只要选行约束的列和列约束的列就行了(第90行);结束的条件也由稀疏矩阵为空变成了行列约束为空(第85行)。

另外这个题非常无聊的一点是问过的问题可能还会再问,所以打表是必须的。


#include <cstdio>
#include <cstring>

static const int maxm = 1000, maxn = 600, maxnode = maxm*maxn;
int mat[maxm][maxn], N, m, n;
int pres[11];

int UU[maxnode], LL[maxnode], L[maxnode], R[maxnode], U[maxnode], D[maxnode];
int count[maxm+1], head, res;

void remove(int now){
  int ll = LL[now];
  for (int t = R[ll]; t != ll; t = R[t]){
    L[R[UU[t]]] = L[UU[t]];
    R[L[UU[t]]] = R[UU[t]];
    for (int p = D[UU[t]]; p != UU[t]; p = D[p])
      for (int q = R[p]; q != p; q = R[q]){
        count[UU[q]]--;
        D[U[q]] = D[q];
        U[D[q]] = U[q];
      }
   }
}

void resume(int now){
  int ll = LL[now];
  for (int t = R[ll]; t != ll; t = R[t]){
    L[R[UU[t]]] = UU[t];
    R[L[UU[t]]] = UU[t];
    for (int p = D[UU[t]]; p != UU[t]; p = D[p])
      for (int q = R[p]; q != p; q = R[q]){
        count[UU[q]]++;
        D[U[q]] = q;
        U[D[q]] = q;
      }
  }
}

void init(){
  int tp = 0;
  head = tp++;
  for (int i = 0; i < n; ++i){
    int now = tp++;
    count[now] = 0;
    UU[now] = U[now] = D[now] = now;
    L[now] = L[head];
    LL[now] = R[now] = head;
    R[L[head]] = now;
    L[head] = now;
  }

  for (int j = 0; j < m; ++j){
    int now = tp++;
    LL[now] = L[now] = R[now] = now;
    U[now] = U[head];
    UU[now] = D[now] = head;
    D[U[head]] = now;
    U[head] = now;
  }

  for (int i = 0; i < m; ++i){
    for (int j = 0; j < n; ++j){
      if (!mat[i][j]) continue;
      int now = tp++;
      int ll = i+n+1, uu = j+1;
      
      U[now] = U[uu];
      D[now] = uu;
      D[U[uu]] = now;
      U[uu] = now;
      UU[now] = uu;

      L[now] = L[ll];
      R[now] = ll;
      R[L[ll]] = now;
      L[ll] = now;
      LL[now] = ll;

      ++count[uu];
    }
  }
}

void dfs(){
  if (R[head] > 2*N || R[head] == head) {
    ++res;
    return;
  }
  int c = R[head];
  for (int t = R[c]; t != head && t <= 2*N; t = R[t])
    if (count[t] < count[c]) c = t;
  for (int u = D[c]; u != c; u = D[u]){
    remove(u); 
    dfs();
    resume(u);
  }
}

int main(){
  memset(pres,-1,sizeof(pres));
  while (scanf("%d", &N) && N){
    if (pres[N] >= 0){
      printf("%d\n", pres[N]);
      continue;
    }
    
    memset(mat, 0, sizeof(mat));
    memset(count, 0, sizeof(count));
    memset(L,0,sizeof(L));
    memset(R,0,sizeof(R));
    memset(U,0,sizeof(U));
    memset(D,0,sizeof(D));
    res = 0;
    m = N*N;
    n = N*6-2;
    for (int i = 0; i < N*N; ++i){
      int r = i/N, c = i%N;
      mat[i][r] = mat[i][c+N] = mat[i][r+c+N*2] = mat[i][r-c+N*5-2] = 1;
    }
    init();
    dfs();
    printf("%d\n", pres[N] = res);
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值