百练/ 北京大学2016研究生推免上机考试(校外)D: 棋盘问题(递归)

题目来源:http://noi.openjudge.cn/ch0205/323/

323:棋盘问题

总时间限制:1000ms  内存限制: 65536kB

描述

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

输入

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

输出

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

样例输入

2 1

#.

.#

4 4

...#

..#.

.#..

#...

-1 -1

样例输出

2

1

来源

蔡错@pku

-----------------------------------------------------

解题思路

网上好多博客都说要用深搜,但是最后还是用的自己的思路AC的,在百练的测试数据上用时4ms, 在poj的测试数据上用时297ms, 好像确实比深搜要慢一点。但是怎么说呢,对于自己来讲,自己的思路终归是清楚一点。

      就是递归,每放一个棋子,就从棋盘上把这行这列删掉得到一个小棋盘,再对小棋盘计算一遍。为了避免重复(棋子都是一样的),在新的小棋盘上把这枚棋子上面的所有行都置为’*’,表示这些情况之前计算过了。还有就是每次只要遍历前(n-k+1)行,因为再往后剩下的行数比k还小,肯定放不下了。嗯,差不多就是这样。

-----------------------------------------------------

代码

//D:棋盘问题
//总时间限制: 1000ms 内存限制: 65536kB
//描述
//在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
//输入
//输入含有多组测试数据。
//每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
//当为-1 -1时表示输入结束。
//随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
//输出
//对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
//样例输入
//2 1
//#.
//.#
//4 4
//...#
//..#.
//.#..
//#...
//-1 -1
//样例输出
//2
//1

#include<iostream>
#include<fstream>
using namespace std;

int fac(int k)
{
	int i = 1, ans = 1;
	for (i=1; i<=k; i++)
	{
		ans *= i;
	}
	return ans;
}


int compute(char **mat, int n, int k)
{
	int ii,i,j,jj,kk,kkk,cnt=0;
	if (k==1)
	{
		for (i=0; i<n; i++)
		{
			for (j=0; j<n; j++)
			{
				if (mat[i][j] == '#')
				{
					cnt++;
				}
			}
		}
		return cnt;
	}
	if (n==1)
	{
		return 0;
	}
	char ** mat1 = new char*[n-1];
	for (i=0; i<n-1; i++)
	{
		mat1[i] = new char[n-1];
	}
	kk = 0; kkk = 0;
	for (ii = 0; ii<=n-k; ii++)
	{
		for (i=0; i<n; i++)
		{
			if (mat[ii][i] == '#')
			{
				kkk = 0;
				for (j=0; j<n; j++)
				{
					if (j>ii)
					{
						kk = 0;
						for (jj=0; jj<n; jj++)
						{
							if (jj != i)
							{
								mat1[kkk][kk] = mat[j][jj];
								kk++;
							}
						}
						kkk++;
					}
					else if (j<ii)
					{
						for (jj=0; jj<n-1; jj++)
						{
							mat1[kkk][jj] = '.';
						}
						kkk++;
					}

				}
				cnt += compute(mat1,n-1,k-1);
			}
		}
	}
	for (i=0; i<n-1; i++)
	{
		delete[] mat1[i];
	}
	delete[] mat1;
	return cnt;
}


int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin("tm201602D.txt");
	int n,k,i,j;
	fin >> n >> k;
	while (n != -1 && k != -1)
	{
		char ** mat = new char*[n];
		for (i=0; i<n; i++)
		{
			mat[i] = new char[n];
		}
		for (i=0; i<n; i++)
		{
			for (j=0; j<n; j++)
			{
				fin >> mat[i][j];
			}
		}
		int ans = compute(mat,n,k);
		cout << ans << endl;
		for (i=0; i<n; i++)
		{
			delete[] mat[i];
		}
		delete[] mat;
		fin >> n >> k;
	}
	fin.close();
	return 0;
#endif
#ifdef ONLINE_JUDGE
	int n,k,i,j;
	cin >> n >> k;
	while (n != -1 && k != -1)
	{
		char ** mat = new char*[n];
		for (i=0; i<n; i++)
		{
			mat[i] = new char[n];
		}
		for (i=0; i<n; i++)
		{
			for (j=0; j<n; j++)
			{
				cin >> mat[i][j];
			}
		}
		int ans = compute(mat,n,k);
		cout << ans << endl;
		for (i=0; i<n; i++)
		{
			delete[] mat[i];
		}
		delete[] mat;
		cin >> n >> k;
	}
	return 0;
#endif
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值