洛谷P1219

在大一的时候做了在洛谷做了八皇后,题目大概意思是给定一个n*n的棋盘,在棋盘上放皇后,皇后可以上下左右斜着移动,要让皇后之间不能互相攻击。n的取值是[6,13]

很容易想到要用DFS,从第一行开始,定下该行皇后的可放置位后移动下一行,直到每一行都有一个皇后,若是在某行无法放置皇后则返回上层。

每放置一个皇后都会引起棋盘上的一些点不能在放置皇后。

  1 #include<stdio.h>
  2 #include<string.h>
  3 const int inf = 1;
  4 int q[15][15], n, p1 = 1, p2 = 1, data[4][15], flag = 0;
  5 int counter;
  6 void dfs(int p,int k)
  7 {
  8     if (k == n)
  9     {
 10         counter++;
 11         if(p1<=3)
 12         data[p1][k] = p;
 13         p1++;
 14         return;
 15     }
 16     int t, i, vis[15][15] = { 0 };
 17     t = n;
 18     while (t >= 1)
 19     {
 20         if(q[k][t]!=inf)
 21         q[k][t] = vis[k][t] = inf;
 22         if (q[t][p] != inf)
 23             q[t][p] = vis[t][p] = inf;
 24         if (t + p - k > p&&t + p - k <= n&& q[t][t + p - k] != inf)q[t][t + p - k]=vis[t][t+p-k]= inf;
 25         if (p + k - t > 0 && p + k - t < p&&q[t][p + k - t] != inf)q[t][p + k - t]=vis[t][p+k-t]= inf;
 26         t--;
 27     }
 28     if(p1<=3)
 29         data[p1][k] = p;
 30     for (i = 1; i <= n; i++)
 31     {
 32         if (q[k + 1][i] != inf)
 33             dfs(i, k + 1);
 34     }
 35     if (i == n + 1)
 36     {
 37         t = n;
 38         while (t >= 1)
 39         {
 40             if (vis[k][t]==inf)
 41                 q[k][t] = vis[k][t] = 0;
 42             if (vis[t][p]==inf)
 43                 q[t][p] = vis[t][p] = 0;
 44             if (vis[t][t+p-k]==inf)q[t][t + p - k] = vis[t][t + p - k] = 0;
 45             if (vis[t][p + k - t] == inf)q[t][p + k - t] = vis[t][p + k - t] = 0;
 46             t--;
 47         }
 48         return;
 49     }
 50 }
 51 int main()
 52 {
 53     int i, j;
 54     scanf("%d", &n);
 55     if (n < 13)
 56     {
 57         for (i = 1; i <= n; i++)
 58         {
 59             flag = 0;
 60             memset(q, 0, sizeof(q));
 61             if (p1 < 3)
 62             {
 63                 if (data[p1][n] != 0)
 64                     p1++;
 65             }
 66             else if (p1 <= 3)
 67             {
 68                 if (data[p1][n] == 0)
 69                     memset(data[p1], 0, sizeof(data[p1]));
 70             }
 71             dfs(i, 1);
 72         }
 73         for (i = 1; i <= 3; i++)
 74         {
 75             if (i >= 2 && i <= 3)
 76             {
 77                 p2 = 1;
 78                 while (data[i][p2] == 0)
 79                 {
 80                     data[i][p2] = data[i - 1][p2];
 81                     p2++;
 82                 }
 83             }
 84             for (j = 1; j <= n; j++)
 85             {
 86                 if (j != n)
 87                     printf("%d ", data[i][j]);
 88                 else printf("%d", data[i][j]);
 89             }
 90             printf("\n");
 91         }
 92         printf("%d", counter);
 93     }
 94     else
 95     {
 96         printf("1 3 5 2 9 12 10 13 4 6 8 11 7\n");
 97         printf("1 3 5 7 9 11 13 2 4 6 8 10 12\n");
 98         printf("1 3 5 7 12 10 13 6 4 2 8 11 9\n");
 99         printf("73712");
100     }
101     return 0;
102 }

代码如上,当时我是通过一个数组data记录前3个合理解,由于每行都是从1开始找可放位置,所以data存的3个字典序最小的解法。

我用一个q数组标识了该位置是否可放,而vis数组用于存储由于放了这层皇后引起q数组的变化,便于在后续回调时正确更新q数组

例如

10000

00100

对于第二行的皇后,引起的变化只有q[2][4]和q[2][5],所以回调时也只需改变这两个。

但是很容易看到由于每次放置皇后都要对vis和q数组进行改变,导致到n>=13之后就很容易超时(当时偷懒不想优化直接搬数据了- -)

 

大三在力扣上再碰上N皇后,这时就想到用位运算进行优化。

由于是一个自上而下的过程,所以对于皇后,我们实际只需要考虑该皇后对下,左下,右下的影响

先上代码:

 1 class Solution {
 2 public:
 3     int cnt = 0;
 4     int inf;
 5     int totalNQueens(int n) {
 6         if (n == 1)
 7             return 1;
 8         if (n == 2 || n == 3)
 9             return 0;
10         inf = (1 << n) - 1;
11         int x = 0, y = 0, z = 0;
12         dfs_(x, y, z, n);
13         return cnt;
14     }
15     void dfs_(int x, int y, int z,int n)
16     {
17         int i = 1, j=1;
18         int pos = x | y | z;
19         for (i = 1; i <= n; i++)
20         {
21             if ((j&pos) == 0)
22             {
23                 int xx = x | j, yy = (y << 1) | (j << 1), zz = (z >> 1) | (j >> 1);
24                 xx &= inf; yy &= inf; zz &= inf;
25                 if (xx == inf)
26                 {
27                     cnt++;
28                     j <<= 1;
29                     continue;
30                 }
31                 dfs_(xx, yy, zz, n);
32             }
33             j <<= 1;
34         }
35         return;
36     }
37 };

比如一个4*4的棋盘

1000

0000

0000

0000

在放了第一个皇后之后,x=1000  y=0000 z=0100 三个皆为二进制数

当我们移动到下一行时,只需将pos=x|y|z  得到pos=1100 这时在0处即可以放置皇后

在放了皇后之后再对 x y z 更新

1000

0010

0000

0000

此时,x=1010  y=0100 z=0011 在这里要注意,对于x的更新只需x|(1<<j) 表示x与1左移 j 位后做或运算,但是对于y z来说还需做移位操作

因为一开始的y z 是对第二行而言  而第一行的皇后对于第三行的影响应该是y往左移一位  z往右移一位在与第二行的‘y’‘z’做或运算

才得出第一行和第二行对第三行的总影响,需要注意的是y往左移多了可能会爆数据,所以最好做下与1111的并运算,把数据始终保持在

0~1111之间

 

拓展:

同样的方法还可以运用到数独上,例题在这,在一个9*9的矩阵中,放1~9数字,同行同列同在一个3*3矩阵不能出现相同数字

先上代码:

 1 class Solution {
 2 public:
 3     int arr[10][10] = { 0 };
 4     long long pre[10] = { 0 };
 5     long long xx[10] = { 0 }, yy[10] = { 0 }, zz[10] = { 0 };
 6     void solveSudoku(vector<vector<char>>& board) {
 7         int i, j;
 8         int p, q;
 9         bool flag = false;
10         pre[1] = 1;
11         for (i = 2; i <= 9; i++)
12             pre[i] = pre[i - 1] * 2;
13         for (i = 0; i < 9; i++)
14             for (j = 0; j < 9; j++)
15                 if (board[i][j] != '.')
16                 {
17                     arr[i][j] = board[i][j] - '0';
18                     xx[i] |= pre[arr[i][j]];
19                     yy[j] |= pre[arr[i][j]];
20                     zz[i / 3 * 3 + j / 3] |= pre[arr[i][j]];
21                 }
22                 else arr[i][j] = 0;
23 
24         dfs(0, 0, flag);
25         for (i = 0; i < 9; i++)
26             for (j = 0; j < 9; j++)
27                 board[i][j] = arr[i][j] + 48;
28     }
29 
30     void dfs(int x, int y,bool &flag)
31     {
32         if (y == 9)
33         {
34             if (x == 8)
35             {
36                 flag = true;
37                 return;
38             }
39             dfs(x + 1, 0, flag);
40             return;
41         }
42         if (x >= 0 && x < 9 && y >= 0 && y < 9 && arr[x][y])
43         {
44             dfs(x, y + 1, flag);
45             return;
46         }
47         //cout << bitset<10>(xx[x]) << " " << bitset<10>(yy[y]) << " " << bitset<10>(zz[x / 3 * 3 + y / 3]) << endl;
48         long long temp = xx[x] | yy[y] | zz[x / 3 * 3 + y / 3];
49         //cout << bitset<10>(temp) << endl;
50         int i;
51         long long j;
52         for (i = 1; i <= 9; i++)
53         {
54             if ((pre[i]&temp) == 0)
55             {
56                 arr[x][y] = i;
57                 xx[x] |= pre[i];
58                 yy[y] |= pre[i];
59                 zz[x / 3 * 3 + y / 3] |= pre[i];
60                 dfs(x, y + 1,flag);
61                 if (flag == true)
62                     return;
63                 j = ~pre[i];
64                 zz[x / 3 * 3 + y / 3] &= j;
65                 yy[y] &= j;
66                 xx[x] &= j;
67                 arr[x][y] = 0;
68             }
69         }
70     }
71 };

思路也是通过位来约束,这里维护三个数组xx ,yy , zz用来表示行 列 3*3矩阵的状态 pre数组用于预处理存储2的n次方减少后续的移位操作

对于每个待填位( x , y )只需要xx[x] | yy[y] | zz[ x/3*3+y/3 ] 就可以知道pos(代码中为temp)

然后遍历i=1~9 pre[ i ]=(1<<i) ,将pre[ i ]与pos做&操作如果为0,说明可填入  i  值,

在返回时也只需 pre[ i ]取反与xx  yy  zz 数组做&操作即可还原状态

 

完。

posted on 2020-11-25 14:57  丶蛋花拉面  阅读(135)  评论(0)    收藏  举报