POJ1321:棋盘问题

点击打开题目链接


棋盘问题

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 17474 Accepted: 8631

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行取状态rowstF[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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值