原文地址:点击打开链接
在n*n(n≤20)的方格棋盘上放置n 个车(可以攻击所在行、列),求使它们不能互相攻击的方案总数。仅供和我一样的菜鸟们参考
以n=4为例子解析源码
#include <iostream>
#include <cmath>
using namespace std;
__int64 a[1100000];
int main()
{
__int64 n;
while (cin >> n){
memset(a, 0, sizeof(a));
a[0] = 1;
for (int i = 1; i <= 1<<n; i ++){//注意这里是1左移n位不是n<<1,显然这里是在枚举0000~1111的每一种状态
for (int j = i; j > 0; j -= (j&-j)){//首先注意这里是倒推,因为要由之前的状态推出现在的状态。
a[i] += a[i&~(j&-j)]; //这里的位运算处理甚是漂亮,它可以保证每一次都刚好取到i的子集 ,首先j&-j可以得出在i之前的每一种状态j的最低位1的位置k,然后取反可以保证只有第k个位置刚好为0,那么求与之后就在原来i的基础上去除了第k个1
//比如说当前i枚举到0111,那么j&-j = 0001,则~(j&-j) = 1110,那么i&1110 = 0110,0110就是0111的一个子集,随后去掉当前最低位k,j变成0110,以此反复运算,直到j=0000
}
}
cout<<a[(1<<n)-1]<<endl;
}
return 0;
}
前一个状态压缩的升级版
在n*n(n≤20)的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互相攻击的方案总数。
和前面差别不大,主要是加了一个不可达点的限制,那么用二进制记录每一个不可达点然后还是按照以前的进行位运算,我写的代码不知道对不对,只核对了3以下的数据,希望路过神牛予以点评或修改
在n*n(n≤20)的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互相攻击的方案总数。
和前面差别不大,主要是加了一个不可达点的限制,那么用二进制记录每一个不可达点然后还是按照以前的进行位运算,我写的代码不知道对不对,只核对了3以下的数据,希望路过神牛予以点评或修改
#include <iostream>
#include <cmath>
using namespace std;
__int64 a[1100000];
int is_allow[20];//用位记录所有不能放置棋子的位置
int counter[21][21];
/*void turn(int x, int n)//转换成二进制输出,属测试代码
{
int t = x;
int num= 0;
int xx[100];
while (x){
xx[num++] = x%2;
x/=2;
}
for (int i = num; i < n; i ++)cout<<0;
for (int i = num-1; i >=0 ; i --)cout<<xx[i];
cout<<endl;
}*/
void chang(int n)//将输入的地图转化到is_allow中
{
for (int i = 1; i <= n; i ++){
int sum = 0;
for (int j =n; j >= 1; j --){//转化成十进制存储到is_allow中
sum += counter[i][n-j+1]*(int)pow(2.0, n-j);
}
is_allow[i] = sum;
}
}
int main()
{
__int64 n;
while (cin >> n){
memset(is_allow, 0, sizeof(is_allow));
memset(counter, 0, sizeof(counter));
int t;
scanf("%d", &t);
for (int i = 0; i < t; i ++){
int x, y;
scanf("%d%d", &x, &y);
counter[x][y] = 1;//记录不能放置棋子的位置
}
chang(n);//转化
memset(a, 0, sizeof(a));
a[0] = 1;
for (int i = 1; i <= 1<<n; i ++){
int cc = 0, tt = i;
while (tt)tt&=(tt-1),cc ++;//cc记录当前放到了第几行
int tmps = i^is_allow[cc];//在i中去除不能放置的位置
for (int j = tmps; j > 0; j -= (j&-j)){
a[i] += a[i&~(j&-j)];
}
}
printf("%I64d", a[(1<<n)-1]);
}
return 0;
}