XTU1378Blocks

dp题光看代码可能比较抽象(本菜鸡是这样的),所以我讲的略小白一点,大佬就别看了orz

题目描述

给你一个n块积木,每个积木块都是立方体,现在把它们排列一排,成m列,要求每列上至少有1个积木,且从左到右,每列的积木数量呈严格单调下降。比如8块积木,排成3列,那么合法的安排方案为521或者431。请问n块积木按规则排成m有多少种不同的方案?

输入

第一行是一个整数T(1≤T≤1000),表示样例的个数。

以后每个样例占一行,为两个整数 n(1≤n≤100),m(1≤m≤10)。

输出

依次每行输出一个样例的结果,为一个整数。

样例输入

2
8 3
13 4

样例输出

2
3

样例解释

第二个样例的合法方案为7321,6421,5431

先不多说,我喜欢先放代码。代码略丑(见谅)

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<bits/stdc++.h>
using namespace std;

int main() {
	int t;
	int i, j, k, l, n, m,sum;
	scanf("%d", &t);
	while (t--) {
		int a[101][11][110];//a[i][j][k]为i块积木,共j列,第一列为k个方块的方案数
		memset(a, 0, sizeof(a));
		a[1][1][1] = 1;
		scanf("%d %d", &n, &m);
		for (i = 2; i <= n; i++) {
			for (j = 1; j <= m; j++) {
				for (k = 1; k <= i; k++) {
					if (i == k && j == 1) { //如果排成一列,第一列为总积木时,只有一种情况 
						a[i][j][k] = 1;
					} else {
						for (l = k - 1; l >= 1; l--) {
							a[i][j][k] += a[i - k][j - 1][l];
						}
					}
				}
			}//排几列
		}
		sum = 0;
		for (i = 1; i <= n; i++) {
			sum += a[n][m][i];
		}
		printf("%d\n", sum);
	}
	return 0;
}

举个栗子(手动dp):

有6个积木,要排3列,sum=a[6][3][1]+a[6][3][2]+a[6][3][3]+...a[6][3][6]

而a[6][3][2]=a[4][2][1],a[4][2][1]=a[3][1][0]=0

a[6][3][3]=a[3][2][1]+a[3][2][2],a[3][2][2]=a[1][1][1]=1

a[6][3][4]=a[2][2][1]+a[2][2][2]+a[2][2][3],a[2][2][1]=a[1][1][0]=0

...后面的更不可能了

这个搭建过程:先搭第一列,第一列数字确定下来之后,sum=第二列的情况(记得把总积木和列数都要减去相应的),即第二列分别为1,2,3...n-第一列的积木。

而第二列的每一种的情况又分别=第三列的情况...

这样一直继续下去,就是一个dp过程。

可以总结出方程a[i][j][k]=a[i-k][j-1][1]..........+a[i-k][j-1][k-1]

这里的k是可以优化的,上面的栗子中可以看见,有一些k完全是不可能存在的,都已经超过总积木,或者有些列已经为0了,具体优化留给你们想叭。

代码思想:

确定下核心方程之后,就是依次枚举i,j,k,因为dp的终点我们应该是确定的,就是排成一列,第一列就是总积木时,这时的情况是1。而其他情况都可以用方程运算。

这边可以把整个搭建过程放到while函数的外面,再输入输出,这样代码会更好看啦(但是我懒)。

如果还是有点晕的欢迎留言讨论,然后要是说错了欢迎大佬捉虫QwQ

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值