在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
对于每一组数据,给出一行输出,输出摆放的方案数目
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
Sample Output
2 1
Source
[Submit] [Go Back] [Status] [Discuss]
/*
非常棒的解法:状态压缩DP!!
关键点:
1.(j&(1<<(u-1)))==0)的判断决定了dp[i][j]是否=0
即有.(不可放旗位子)时,它决定了在#可以放,即dp[i][j]不为0
2.这里的二进制是倒过来的ex:一般是3=011;这里3=110
因为要与图配合+方便表示(当然你也可以把它弄成一般的(1<<(u-1))改成(1<<(n-u))即可
//*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define NN 300
int ill[12][NN],num[NN];
int dp[12][NN];
//dp[i][j]:1到i行已覆盖j列的方案数
int i,j,l,n,k,status,newst,ans,u;
char g[12][12];
void cal(int now){
int i,j,tmp,flag,cnt;
tmp=1;
cnt=0;
for(i=1;i<=n;++i){
if (now&tmp) cnt+=1;
tmp=tmp<<1;
}
num[now]=cnt;
}
void pbinary(int tj)
{
int flag=0,bn=tj;
while(tj)
{
if(tj&1)cout<<1;
else cout<<0;
tj/=2;
flag++;
}
while(flag<n)
{
cout<<0; flag++;
}cout<<" ---> "<<bn<<endl;
}
int main()
{
//freopen("in.h","r",stdin);
while(1)
{
scanf("%d%d",&n,&k);
if (n==-1&&k==-1) break;
for(i=1;i<=n;++i) scanf("%s",g[i]+1);
status=1<<n;
for(i=0;i<status;++i) cal(i);
//num[i]存的是i对应2进制的1的位数(即表已经放了num[i]个旗子在棋盘)
//代表的是每一行可以放的状态.如n=2有4种状态(用0~3的二进制表示)
//for(i=0;i<status;++i) cout<<num[i]<<" ";cout<<endl;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(i=1;i<=n;++i)
{
for(j=0;j<status;++j)
if (num[j]<=k)//(棋盘已放num[j]个旗子<=要放)
{
dp[i][j]+=dp[i-1][j]; //+=:下面产生的或会影响f[i][j]
//当前行不加棋子(也相当于把上次状态转移到当前)
//若上一行的j状态还未出现,则有dp[i-1][j]=0
for(u=1;u<=n;++u)
if ((g[i][u]=='#')&&(j&(1<<(n-u)))==0)
{
//(1<<(u-1))表把第u位转为二进制,比如:u=3;(1<<(u-1))=4即二进制100倒数第3个(即第一个)为1
//目的是和状态j(1表已放位置)的二进制比较,看是否不在同列
//newst=j|(1<<(u-1));
newst=j|(1<<(n-u));
dp[i][newst]+=dp[i-1][j];
/*pbinary(j);
pbinary(1<<(u-1));
pbinary(newst);//*/
//若上一行的j状态还未出现,则有dp[i-1][j]=0
//当前行加棋子,由j状态到newst状态,故f[i-1,j]+到f[i,newst]
}
}
}
for(ans=i=0;i<status;++i)
if (num[i]==k) {ans+=dp[n][i];}
//最后将最后一行棋子数等于k的状态的方案数求和即可。
printf("%d\n",ans);
}
return 0;
}
//c框from:http://blog.csdn.net/asdfgh0308/article/details/8454022
</pre><pre name="code" class="cpp">网上满天下的dfs,这里就不多说了
<pre name="code" class="cpp">#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,ans,flag;
int vis[11][11],a[11][11],c[11];
char s[11][11];
void dfs(int x,int de)
{
if(de==m)
{
flag++;
return ;
}
int i,j;
for(i=x+1;i<=n;i++) //防止一行都不是旗位
{
for(j=1;j<=n;j++)
{
if(c[j] || a[i][j]==0)continue;
c[j]=1; //放
dfs(i,de+1);
c[j]=0; //不放
}
}
}
int main()
{
int i,j;
while(~scanf("%d%d",&n,&m))
{
if(n==-1 && m==-1)break;
memset(c,0,sizeof(c));
memset(a,0,sizeof(a));
flag=0;
for(i=1;i<=n;i++)
{
scanf("%s",s[i]);
for(j=0;j<strlen(s[i]);j++)
if(s[i][j]=='#')a[i][j+1]=1;
}
dfs(0,0);
printf("%d\n",flag);
}
return 0;
}