【问题描述】
地主某君有一块由2×n个栅格组成的土地,有k个儿子,现在地主快要终老了,要把这些土地分给这些儿子。
分给每个儿子的土地最小的单位是一个栅格,同时,分给同一个儿子的土地要求要相邻连续的。
地主觉得分给某个儿子的土地面积至少有一个栅格,但是具体多少可以随意。
请问,聪明的你,能够算出地主一共有多少种分土地的方法吗?也就是说要求把2×n的栅格分成K个连通区域,每个区域至少有一个栅格。
【输入格式】
包含两个正整数n和K。
【输出格式】
包含一个整数,为可以分土地的方法数模100000007。
【输入输出样例一】
field.in
2 1
field.out
1
【输入输出样例二】
field.in
5 2
field.out
45
【数据范围】
对于100%的数据:1<=n<=1000,1<=K<=2×n。
【分析】动态规划 + 分类讨论
- 设
f[i][k][0/1]
表示已经取了第
1
~
i 行,分为 k 个连通区域,状态为0 或 1 时的总方案数 - 对于状态
0/1 ,我们有如下定义:
[1]、状态 0 表示第i 行的两个栅格分属于两个不同的连通区域
[2]、状态 1 表示第i 行的两个栅格属于同一个连通区域 则状态转移方程为:(这里有一点要注意:这个儿子选这块地,那个儿子选那块地,把两个儿子所选的地交换,仍只算一种方案)
[1]、 f[i][k][0]+=f[i−1][k−2][0]
[2]、 f[i][k][0]+=f[i−1][k−2][1]
[3]、 f[i][k][0]+=f[i−1][k−1][1]×2
[4]、 f[i][k][0]+=f[i−1][k−1][0]×2
[5]、 f[i][k][0]+=f[i−1][k][0][6]、 f[i][k][1]+=f[i−1][k−1][0]
[7]、 f[i][k][1]+=f[i−1][k−1][1]
[8]、 f[i][k][1]+=f[i−1][k][0]×2
[9]、 f[i][k][1]+=f[i−1][k][1]- 转移是如何得到的?这是根据第
i
行与第
i−1 行的四个栅格所属连通区域的关系决定,我们通过以下的图来说明(颜色的不同表示所属于不同的连通区域,方块下方的编号表示所对应的转移)
- 对于 DP 的初值,我们直接考虑第一行的摆放方案,即为 f[1][1][1]=f[1][2][0]=1
- 则最后答案 Answer=f[n][K][1]+f[n][K][0] ,复杂度为 O(nK)
【代码】
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int Mod = 100000007;
const int N = 1005;
int f[N][N << 1][2], n, d;
inline int Min(const int &x, const int &y) {return x < y ? x : y;}
int main()
{
freopen("field.in", "r", stdin);
freopen("field.out", "w", stdout);
scanf("%d%d", &n, &d);
f[1][2][0] = f[1][1][1] = 1;
for (int i = 2; i <= n; ++i)
for (int k = 1; k <= Min((i << 1), d); ++k)
{
(f[i][k][0] += (f[i - 1][k - 1][1] << 1)
+ (f[i - 1][k - 1][0] << 1)
+ f[i - 1][k - 2][0]
+ f[i - 1][k - 2][1]
+ f[i - 1][k][0]) %= Mod;
(f[i][k][1] += (f[i - 1][k][0] << 1)
+ f[i - 1][k - 1][0]
+ f[i - 1][k - 1][1]
+ f[i - 1][k][1]) %= Mod;
}
printf("%d\n", (f[n][d][0] + f[n][d][1]) % Mod);
fclose(stdin); fclose(stdout);
return 0;
}