题目链接:[点这儿].
题意:
![]()
一个棋盘,每个格子可以放一个棋子,放了棋子的这一行和这一列就不能再放其他棋子了,问你对于规模为n
的棋盘,上面放k
颗棋子有多少种放法.其中 1<=n<=30,0<=k<=n2 .
解析:
首先,这个题组合放法可以做,而且比较简单,待会会给出,现在我们说说动态规划的做法.
这是一个简单递推动态规划,定义
dp[i][j][k]
为规模为iXj
的棋盘上面放k
颗棋子的方法数,那么根据下面这些图就很容易得出状态转移方程.
说明一下这个图,红色格子会放一个棋子,白色和绿色格子不会放棋子,橙色格子区是放好某些数量的棋子的区域,而灰色格子是可以放棋子但是没放的区域,
- a :格子
(i,j)
放一个棋子,故dp[i]j][k] += dp[i - 1][j - 1][k - 1]
;- b : 白色的格子不放棋子,那么棋子都集中在橙色区域,故
dp[i][j][k] += dp[i - 1][j - 1][k]
;- c : 灰色区域加红色格子可以放一个棋子,不过这样会导致出现绿色区域,故
dp[i][j][k] += (j - 1) * dp[i - 1][j - 2]
;- d : 情况同c ,故
dp[i][j][k] += (i - 1) * dp[i - 2][j - 1]
;- e : 横着的灰色区域加红色格子和竖着的灰色区域加红色格子,各可以放一个棋子,这样就会导致出现e 中的绿色区域,故
dp[i][j][k] += (i - 1) * (j - 1) * dp[i - 2][j - 2][k - 2]
。综上,得出状态转移方程:
dpi,j,k=dpi−1,j−1,k−1+dpi−1,j−1,k+(j−1)∗dpi−1,j−2,k−1+(i−1)∗dpi−2,j−1,k−1+(j−1)∗(i−1)∗dpi−2,j−2,k−2
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 30
typedef long long LL;
LL dp[N + 1][N + 1][N + 1];
void init()
{
for (int i = 0; i <= N; i++)
for (int j = 0; j <= N; j++)
dp[i][j][0] = 1;
for (LL k = 1; k <= N; k++)
for (LL i = 1; i <= N; i++)
for (LL j = 1; j <= N && k <= max(i, j); j++)
dp[i][j][k] = dp[i - 1][j - 1][k - 1] + dp[i - 1][j - 1][k] +
(j - 1) * dp[i - 1][j - 2][k - 1] + (i - 1) * dp[i - 2][j - 1][k - 1] +
(j - 1) * (i - 1) * dp[i - 2][j - 2][k - 2];
}
int main()
{
init();
int T, K = 1;
for (scanf("%d", &T); T--; K++) {
int n, k;
scanf("%d%d", &n, &k);
printf("Case %d: %lld\n", K, k <= n ? dp[n][n][k] : 0);
}
return 0;
}
其他方法:
组合求和这道题就太简单了,这么想,从
n
行中选k
行,从n
列选k
列出来,这样,选出来的行列唯一构成一个二元组(i,j)
,然后再把这k
个二元组排列一下就可以了.公式:
⎧⎩⎨⎪⎪0(kn)∗(kn)∗n!k>nk<=n
代码就不写了.
注意:
这个题范围给的不是很好,首先我们来看下自己给出的输入输出数据.
输入:
30 1 1 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 16 15 17 16 18 17 19 18 20 19 21 20 22 21 23 22 24 23 25 24 26 25 27 26 28 27 29 28 30 29
AC的输出:
Case 1: 1 Case 2: 4 Case 3: 18 Case 4: 96 Case 5: 600 Case 6: 4320 Case 7: 35280 Case 8: 322560 Case 9: 3265920 Case 10: 36288000 Case 11: 439084800 Case 12: 5748019200 Case 13: 80951270400 Case 14: 1220496076800 Case 15: 19615115520000 Case 16: 334764638208000 Case 17: 6046686277632000 Case 18: 115242726703104000 Case 19: 2311256907767808000 Case 20: -6682192057595854848 Case 21: 2998629330744246272 Case 22: -9067791737139757056 Case 23: 2483266474485481472 Case 24: -3577022814806343680 Case 25: -8604058797746421760 Case 26: -3914123377064804352 Case 27: -484513635729670144 Case 28: -1087798259688144896 Case 29: -1708620176192176128 Case 30: -4682952033483882496
从样例可以看出,当
k=n-1
时数字最大,因此,我给出了上述输入样例,可以发现,当n>19
后就溢出了,我估计后台数据也是这样的,题目中为了避免大整数,特意告诉你“You may safely assume that this number will be less than 1017 .”,你就按这么多来,long long
就够了,我一开始没看到这句话,测试了n=30,k=29
后我都不敢交!