状态压缩DP POJ 1321 棋盘问题

来源:互联网 发布:淘宝卖衣服代理 编辑:程序博客网 时间:2024/06/01 09:22

题目:

Description

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

Input

输入含有多组测试数据。 
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n 
当为-1 -1时表示输入结束。 
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。 

Output

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

Sample Input

2 1#..#4 4...#..#..#..#...-1 -1

Sample Output

21

情不自禁的想起来,以前自己玩游戏,遇到计算量特别大的关卡,玩不过去,可以编程解决的就会编程解决。

最变态的时候写过22层的循环,因为问题那个游戏真的好难好难。。。

于是我写了一个8层的循环。。。。

代码:

#include<iostream>#include<string.h>using namespace std;int list[8];//输入的棋盘状态char c;int sum;int result;//最后的结果int main(){int n, k;while (cin >> n >> k){if (n == -1)break;memset(list, 0, sizeof(list));result = 0;for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){cin >> c;if (c == '#')list[i] += (1 << j);}}for (int i7 = 0; i7 < (1 << n); i7++)//8层循环,要注意顺序,因为如果n<7的话i7只能是0{if (i7 != (i7&(-i7)))continue;if ((i7&list[7]) != i7)continue;for (int i6 = 0; i6 < (1 << n); i6++){if (i6 != (i6&(-i6)))continue;//1行只能放1if ((i6&list[6]) != i6)continue;//只能放在写了'#'的格子里面if (i6&i7)continue;//1列只能放1for (int i5 = 0; i5 < (1 << n); i5++){if (i5 != (i5&(-i5)))continue;if ((i5&list[5]) != i5)continue;if (i5&(i6 | i7))continue;for (int i4 = 0; i4 < (1 << n); i4++){if (i4 != (i4&(-i4)))continue;if ((i4&list[4]) != i4)continue;if (i4&(i5 | i6 | i7))continue;for (int i3 = 0; i3 < (1 << n); i3++){if (i3 != (i3&(-i3)))continue;if ((i3&list[3]) != i3)continue;if (i3&(i4 | i5 | i6 | i7))continue;for (int i2 = 0; i2 < (1 << n); i2++){if (i2 != (i2&(-i2)))continue;if ((i2&list[2]) != i2)continue;if (i2&(i3 | i4 | i5 | i6 | i7))continue;for (int i1 = 0; i1 < (1 << n); i1++){if (i1 != (i1&(-i1)))continue;if ((i1&list[1]) != i1)continue;if (i1&(i2 | i3 | i4 | i5 | i6 | i7))continue;for (int i0 = 0; i0 < (1 << n); i0++){if (i0 != (i0&(-i0)))continue;if ((i0&list[0]) != i0)continue;if (i0&(i1 | i2 | i3 | i4 | i5 | i6 | i7))continue;sum = 0;if (i0)sum++;//整个棋盘放了多少个棋子if (i1)sum++;if (i2)sum++;if (i3)sum++;if (i4)sum++;if (i5)sum++;if (i6)sum++;if (i7)sum++;if (sum == k)result++;}}}}}}}}cout << result << endl}
return 0;
}

(为了把这个代码弄的这么好看,我也是拼了。。。)

很显然,这个方法是很慢很慢的(虽然我AC了,但是这肯定是不行的)

于是我又写了新的代码:

#include<iostream>#include<string.h>using namespace std;int list[8];//输入的棋盘状态int r[8][256];char c;int sum;int k;bool ok(int n){int s = 0;for (int a = 1; a < 256; a *= 2)if (n & a)s++;if (s>k)return false;return true;}int main(){int n;while (cin >> n >> k){if (n == -1)break;memset(list, 0, sizeof(list));memset(r, 0, sizeof(r));for (int i = 0; i < n; i++)//输入{for (int j = 0; j < n; j++){cin >> c;if (c == '#')list[i] += (1 << j);}}for (int i = 0; i < (1 << n); i++)
if ((i&list[0]) == i && ok(i)&&(i&(-i))==i)r[0][i] = 1;for (int i = 1; i < n; i++)for (int j = 0; j < (1 << n); j++)
if (ok(j))for (int kk = 0; kk < (1 << n); kk++)
if ((j&kk) == kk && ((j^kk)&list[i]) == (j^kk) && ((j^kk)&(-(j^kk))) == (j^kk))
r[i][j] += r[i - 1][kk];sum = 0;for (int i = 0; i < (1 << n); i++){int s = 0;for (int a = 1; a < 256; a *= 2)if (i & a)s++;if(s==k)sum += r[n - 1][i];}cout << sum << endl;}return 0;}

主要就是r

r[i][j] 表示的是前i行的状态是 j 的情况数。

比如说,i=4,j=1011,那么前4行的状态是第1,2,4个被取了。(之所以用这样的顺序是因为维度是不确定的,这样方便很多)

可能是第1行取1,第2行取2,第4行取4,

也有可能是第1行取2,第2行取4,第4行取1,等等。。。





1 0
原创粉丝点击