棋盘问题
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 2 1 Source
蔡错@pku
|
=====================================算法分析=====================================
一、DFS:
没啥好说的囧。
二、DP+状态压缩:
令:棋盘上每列的摆放状态为mapst,棋盘上某行的摆放状态为rowst,前i行棋盘取状态mapst的种数为F[i][mapst]。
则:所求答案即为∑F[N][mapst](取状态mapst时摆放的棋子数为M)。
1、网上多数题解的思路:通过枚举rowst由F[i-1][mapst]更新F[i][mapst|rowst]。
第i行不摆放棋子:F[i][mapst]=F[i-1][mapst]。
第i行取状态rowst:F[i][mapst|rowst]+=F[i-1][mapst](mapst&rows==0)。
2、我的思路:通过枚举rowst由F[i-1][mapst&(~rowst)]求解F[i][mapst]。
F[i][maps]=∑F[i-1][mapst&(~rowst)](mapst&rowst!=0)。
注:1、由于F[i]仅与F[i-1]有关所以这里可以用滚动数组优化(此时mapst需要逆序枚举)。
2、求取状态mapst时摆放的棋子数即求二进制整形中1的个数的算法参见:
http://www.cnblogs.com/graphics/archive/2010/06/21/1752421.html
=======================================代码=======================================
一、DFS
#include<stdio.h>
#include<string.h>
int N,M,Ans;
bool HashCol[10];
char Map[10][10];
void DFS(int Row,int Res)
{
if(Res==0) { ++Ans; return; }
if(N<=Row||N-Row+1<Res) { return; }
DFS(Row+1,Res);
for(int Col=0;Col<N;++Col)
{
if(Map[Row][Col]=='#'&&!HashCol[Col])
{
HashCol[Col]=true;
DFS(Row+1,Res-1);
HashCol[Col]=false;
}
}
}
void ReaDate()
{
Ans=0;
memset(HashCol,0,sizeof(HashCol));
for(int i=0;i<N;++i)
{
scanf("%*c%s",Map[i]);
}
}
int main()
{
while(scanf("%d%d",&N,&M)==2&&!(N==-1&&M==-1))
{
ReaDate();
DFS(0,M);
printf("%d\n",Ans);
}
return 0;
}
二、DP+状态压缩
#include<stdio.h>
#include<string.h>
const int MAXS=1<<8;
int N,M,F[MAXS];
char Map[10];
int Count(int Num)
{
int sum;
for(sum=0;Num;++sum)
{
Num&=(Num-1);
}
return sum;
}
int main()
{
while(scanf("%d%d%*c",&N,&M)==2&&!(N==-1&&M==-1))
{
memset(F,0,sizeof(F));
F[0]=1;
for(int i=0;i<N;++i)
{
scanf("%s%*c",Map);
for(int mapst=MAXS-1;mapst>=0;--mapst)
{
for(int j=0;Map[j];++j) if(Map[j]=='#')
{
int rowst=1<<j;
if(mapst&rowst) F[mapst]+=F[mapst&(~rowst)];
}
}
}
int ans=0;
for(int mapst=MAXS-1;mapst>=0;--mapst)
{
if(Count(mapst)==M) ans+=F[mapst];
}
printf("%d\n",ans);
}
return 0;
}