关于的整数划分讨论(考虑顺序与不考虑顺序)

关于整数划分的讨论

整数划分

问一个整数有多少种划分为整数之和的组合。
例如:

4 = 1 + 1 + 1 + 1 4=1+1+1+1 4=1+1+1+1
4 = 2 + 1 + 1 , 4 = 2 + 2 4=2+1+1 ,4=2+2 4=2+1+1,4=2+2
4 = 3 + 1 4=3+1 4=3+1
4 = 4 4=4 4=4

对于这个问题需要使用动态规划来解决
首先定义dp数组
我们定义 d p [ n ] [ m ] dp[n][m] dp[n][m]是数n的不超过m的划分组合数
例如: d p [ 4 ] [ 2 ] = 3 dp[4][2]=3 dp[4][2]=3,其中包含

4 = 1 + 1 + 1 + 1 4=1+1+1+1 4=1+1+1+1
4 = 2 + 1 + 1 , 4 = 2 + 2 4=2+1+1,4=2+2 4=2+1+1,4=2+2

递推式推导:
1、 d p [ n ] [ 1 ] = 1 dp[n][1]=1 dp[n][1]=1,因为任何数的不大于一的划分只能是许多个一的组合。
2、 d p [ n ] [ n ] = d p [ n ] [ n − 1 ] + 1 dp[n][n]=dp[n][n-1]+1 dp[n][n]=dp[n][n1]+1,这个1是指这个数本身这种划分方法。
3、 d p [ n ] [ m ] = d p [ n ] [ n ] ( n < m ) dp[n][m]=dp[n][n](n<m) dp[n][m]=dp[n][n](n<m)n的划分不会超过n,所以更大范围的划分并不会增加组合。
4、 d p [ n ] [ m ] = d p [ n ] [ m − 1 ] + d p [ n − m ] [ m ] ( n > m ) dp[n][m]=dp[n][m-1]+dp[n-m][m](n>m) dp[n][m]=dp[n][m1]+dp[nm][m](n>m)
证明:首先我们以4为例先看看具体的过程

d p [ 4 ] [ 3 ] = 4 , d p [ 4 ] [ 2 ] = 3 , d p [ 1 ] [ 3 ] = 1 dp[4][3]=4,dp[4][2]=3,dp[1][3]=1 dp[4][3]=4,dp[4][2]=3,dp[1][3]=1

可以看到等式成立,我们可将一个1看做一个积木那么4的划分有
在这里插入图片描述
我可以知道的是 d p [ 4 ] [ 3 ] dp[4][3] dp[4][3]一定包含了 d p [ 4 ] [ 2 ] dp[4][2] dp[4][2]的所有情况,那么要找递推式关键是如何表示 d p [ 4 ] [ 3 ] − d p [ 4 ] [ 2 ] dp[4][3]-dp[4][2] dp[4][3]dp[4][2],我们可以发现二者之差是4划分中带有3的情况,如图1+3中的绿色部分,这部分有多少种划分起决于剩余积木的摆放方式,即剩余数的划分方式,当然这部分也不能大于3.
我将这一断言推广到整个整数域,对于 d p [ n ] [ m ] ( n > m ) dp[n][m](n>m) dp[n][m](n>m)一定包含 d p [ n ] [ m − 1 ] dp[n][m-1] dp[n][m1] d p [ n ] [ m ] − d p [ n ] [ m − 1 ] dp[n][m]-dp[n][m-1] dp[n][m]dp[n][m1]一定是n有最大数为m的划分的情况组合数。
于是有

d p [ n ] [ m ] = d p [ n ] [ m − 1 ] + d p [ n − m ] [ m ] dp[n][m]=dp[n][m-1]+dp[n-m][m] dp[n][m]=dp[n][m1]+dp[nm][m]

代码实现

对于给定n与m所求范围不大且所求数量很小时,我们可以通过递归仅计算与结果有关的dp值,来减少运算。

#include<iostream>
using namespace std;

int work(int n, int m) {
	if (m == 1)return 1;
	if (n == m)return work(n, m - 1) + 1;
	else if (m > n)return work(n, n);
	else {
		return work(n, m - 1) + work(n - m, m);
	}
}

int main() {
	int n, m;
	cin >> n >> m;

	cout << work(n, m) << endl;

	system("pause");
	return 0;
}

对于n,m很大且需要多次求解一个范围内的划分数时,可以使用动态规划,来避免重复运算。

#include<iostream>
using namespace std;
const int MAXN = 1e3;
int dp[MAXN][MAXN];

int main() {
	int n, m;
	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (j == 1)dp[i][j] = 1;
			else if (i == j) dp[i][j] = dp[i][j - 1] + 1;
			else if (i < j)dp[i][j] = dp[i][i];
			else dp[i][j] = dp[i][j - 1] + dp[i - j][j];
		}
	}

	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cout << dp[i][j] << ' ';
		}
		cout << endl;
	}

	system("pause");
	return 0;
}

整数的素数划分

问一个整数有多少种划分为素数之和的组合。
例如:

4 = 1 + 1 + 1 + 1 4=1+1+1+1 4=1+1+1+1
4 = 2 + 1 + 1 , 4 = 2 + 2 4=2+1+1 ,4=2+2 4=2+1+1,4=2+2
4 = 3 + 1 4=3+1 4=3+1

这个问题需要我们对递推式进行一下修改,并需要额外维护一个数组。我们要解决问题需要计算出一个对应关系 p [ n ] p[n] p[n]为小于n且最接近n的素数。
我们可以用O(n)的时间复杂度求出p[n]数组来实现对应。如图

有这个数组我们就可以推导递推式了
1、 d p [ n ] [ 1 ] = 1 dp[n][1]=1 dp[n][1]=1
2、 d p [ n ] [ m ] = d p [ n ] [ p [ m ] ] ( m 为 合 数 ) dp[n][m]=dp[n][p[m]](m为合数) dp[n][m]=dp[n][p[m]](m) p [ m ] p[m] p[m]是不大于m的最大的素数,而素数划分只能分出素数所以实际上 m − p [ m ] m-p[m] mp[m]之间的数都是不能出现的。所以扩大的这个范围不会增加组合数。
3、 d p [ n ] [ n ] = d p [ n ] [ n − 1 ] + 1 ( n 是 素 数 ) dp[n][n]=dp[n][n-1]+1(n是素数) dp[n][n]=dp[n][n1]+1(n),如果n是合数可以代入上式。
4、 d p [ n ] [ m ] = d p [ n ] [ p [ m − 1 ] ] + d p [ n − m ] [ m ] ( m 是 素 数 ) dp[n][m]=dp[n][p[m-1]]+dp[n-m][m](m是素数) dp[n][m]=dp[n][p[m1]]+dp[nm][m](m),如果m是合数,可以先代入3、
证明:证明过程与上一题的类似,关键是找到 d p [ n ] [ m ] − d p [ n ] [ p [ m − 1 ] ] dp[n][m]-dp[n][p[m-1]] dp[n][m]dp[n][p[m1]],二者之差一定是有m数的划分的组合。

代码实现

#include<iostream>
using namespace std;
const int MAXN = 1e3;
int dp[MAXN][MAXN];
int p[MAXN];
bool flag[MAXN];
void work(int n) {
	for (int i = 2; i <= n; i++) {
		if (flag[i])continue;
		else {
			int c = i << 1;
			while (c <= n) {
				flag[c] = 1;
				c += i;
			}
		}
	}
	int tmp = 0;
	for (int i = 1; i <= n; i++) {
		if (!flag[i]) tmp = i;
		p[i] = tmp;
	}
}
int main() {
	int n, m;
	cin >> n >> m;
	work(n);
	for (int i = 1; i <= n; i++) 
		cout << p[i] << ' ';
	cout << endl;
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++) {
			if (j == 1)dp[i][j] = 1;
			else if (i == j&&!flag[i]) dp[i][j] = dp[i][j - 1] + 1;
			else if (i < j)dp[i][j] = dp[i][i];
			else if (flag[j])dp[i][j] = dp[i][p[j]];
			else dp[i][j] = dp[i][p[j - 1]] + dp[i - j][j];
		}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cout << dp[i][j] << ' ';
		}
		cout << endl;
	}
	system("pause");
	return 0;
}

整数划分(考虑顺序)

问一个整数有多少种划分为整数之和的排序数
例如:

4 = 1 + 1 + 1 + 1 4=1+1+1+1 4=1+1+1+1
4 = 1 + 1 + 2 , 4 = 1 + 2 + 1 , 4 = 2 + 1 + 1 4=1+1+2,4=1+2+1,4=2+1+1 4=1+1+2,4=1+2+1,4=2+1+1
4 = 2 + 2 4=2+2 4=2+2
4 = 1 + 3 , 4 = 3 + 1 4=1+3,4=3+1 4=1+3,4=3+1
4 = 4 4=4 4=4

考虑顺序的整数划分也是一个动态规划问题,而且递推式要比不考虑顺序的问题简单。
先定义dp数组: d p [ n ] dp[n] dp[n]为n的整数划分排序数
递推式: d p [ n ] = d p [ n − 1 ] + d p [ n − 2 ] + ⋯ + d p [ 1 ] + 1 dp[n]=dp[n-1]+dp[n-2]+\cdots+dp[1]+1 dp[n]=dp[n1]+dp[n2]++dp[1]+1
证明:4=1+三的划分,4=2+二的划分,4=3+一的划分,4=4

整数的素数划分(考虑顺序)

问一个整数有多少种划分为素数之和的排序数
d p [ n ] dp[n] dp[n]为n的素数划分排序数
d p [ n ] = d p [ n − 1 ] + d p [ n − 3 ] + ⋯ dp[n]=dp[n-1]+dp[n-3]+\cdots dp[n]=dp[n1]+dp[n3]+
与上一题不同的是这里只取
4=素数+剩余的划分

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值