Light OJ 1005 - Rooks 数学题解

A rook is a piece used in the game of chess which is played on a board of square grids. A rook can only move vertically or horizontally from its current position and two rooks attack each other if one is on the path of the other. In the following figure, the dark squares represent the reachable locations for rook R1 from its current position. The figure also shows that the rook R1 and R2 are in attacking positions where R1 and R3 are not. R2 and R3 are also in non-attacking positions.

Now, given two numbers n and k, your job is to determine the number of ways one can put k rooks on an n x n chessboard so that no two of them are in attacking positions.

Input

Input starts with an integer T (≤ 350), denoting the number of test cases.

Each case contains two integers n (1 ≤ n ≤ 30) and k (0 ≤ k ≤ n2).

Output

For each case, print the case number and total number of ways one can put the given number of rooks on a chessboard of the given size so that no two of them are in attacking positions. You may safely assume that this number will be less than 1017.

Sample Input

Output for Sample Input

8

1 1

2 1

3 1

4 1

4 2

4 3

4 4

4 5

Case 1: 1

Case 2: 4

Case 3: 9

Case 4: 16

Case 5: 72

Case 6: 96

Case 7: 24

Case 8: 0



本题有点像n-queen的问题的变形,不过和n-queen有本质的不同,因为简化了对角线,那么就可以使用数学的方法求解了。

思考了我好久,终于发现这个是inclusion-exclusion原理的应用。

1 当k等于0的时候为1, 当k等于1的时候,那么就等于n^2

2 可以这样选择的:先选择n^2中的一格,那么就剩下(n-1)^2格可以选择了,然后在选一格,那么又剩下(n-2)^2格选择了

3 这样可以利用乘法原理得到f(n, k) = f(n^2, 1) * f((n-1)^2, 1) * f((n-2)^2, 1)...*f((n-k+1)^2, 1);

4 相当于分别在n, n-1, n-2... (n-k+1)个方格中分别选择一格。

5 但是这样选择有重复,因为选择出来的数不需要排序,那么就把其排序的方法的次数除去,这是根据除法原理计算法

6 最终得到:f(n, k) = f(n^2, 1) * f((n-1)^2, 1) * f((n-2)^2, 1)...*f((n-k+1)^2, 1) / P(k); P(k)是k个数的全排序

例如求f(4, 3) = f(4, 1) * f(3, 1) * f (2,, 1) / 3!;

f(4, 4) = f(4, 1), *f(3, 1), *f(2, 1) / 4!;

因为f(3, 3) = f(3, 1) * f (2, 1) / 3!;

所以可以化简:f(4, 4) = f(3, 3) * f(4, 1) / 4; 最后就利用这个公式,加上动态规划法,可以先计算出一个表,然后直接查表得到答案,速度奇快。

#pragma once
#include <stdio.h>
#include <vector>
using namespace std;

class Rooks1005
{
	const static int SIZE = 31;
	vector<vector<long long> > tbl;

	void genTbl()
	{
		for (int i = 1; i < SIZE; i++)
		{
			tbl[i][0] = 1;
			tbl[i][1] = i * i;
		}

		for (int i = 2; i < SIZE; i++)
		{
			for (int j = 2; j <= i; j++)
			{
				tbl[i][j] = tbl[i][1] * tbl[i-1][j-1] / j;
			}
		}
	}
public:
	Rooks1005():tbl(SIZE, vector<long long>(SIZE))
	{
		genTbl();

		int T, n, k;
		scanf("%d", &T);
		for (int i = 1; i <= T; i++)
		{
			scanf("%d %d", &n, &k);
			if (k > n) printf("Case %d: %d\n", i, 0);
			else printf("Case %d: %lld\n", i, tbl[n][k]);
		}
	}
};




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值