整数划分(动态规划)

本文介绍了完全背包问题的几种解决方案,包括朴素状态表示法的递推公式、优化后的动态规划方法(如f[i][j]=f[i-1][j]+f[i][j-i]),以及使用回滚数组将二维转换为一维。还探讨了另一种状态表示方法,关注能由j个数相加得到总和i的组合数。
摘要由CSDN通过智能技术生成

目录

完全背包写法

1.朴素状态表示法

2.优化写法(1)

3.优化写法(2)

另类方法


完全背包写法

例如

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

这个问题可以转成用n种商品(1~n)其中每种商品的体积就是自己的序号,然后用一个容量是n的背包来存放这些商品,每种物品可以取无数次,问能有多少种装法恰好把背包装满。

这样就把问题转成了类似完全背包的类型,只不过完全背包要求的是最大价值,他的状态选择是max,而整数划分要的是种类,因此考虑的是sum

1.朴素状态表示法

状态表示:f[i][j]表示从1~i种数中组合成j的方法数

状态方程: f[i][j] = f[i-1][j]+f[i-1][j-i]+f[i-1][j-2i]+......

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100;
int f[N][N];//所有从1-i中选总体积恰好是j的选法数量

int main() {
	int n;
	cin >> n;
	f[0][0] = 1; //初始条件0由本身组成
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= n; j++) {
			for (int k = 0; k * i <= j; k++)
				f[i][j] += f[i - 1][j - k * i];
		}
	}
	cout << f[n][n];
	return 0;
}

2.优化写法(1)

f[i][j]   = f[i-1][j]+f[i-1][j-i]+f[i-1][j-2*i]+......
f[i][j-i] =           f[i-1][j-i]+f[i-1][j-2*i]+......
f[i][j]   = f[i-1][j]+f[i][j-i]
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100;
int f[N][N];//所有从1-i中选总体积恰好是j的选法数量

int main() {
	int n;
	cin >> n;
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= n; j++) {
			f[i][j] = f[i - 1][j] + f[i][j - i];
		}
	}
	cout << f[n][n];
	return 0;
}

3.优化写法(2)

回滚数组使得二维变成一维

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100;
int f[N];//所有从1-i中选总体积恰好是j的选法数量

int main() {
	int n;
	cin >> n;
	f[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++) {
			f[j] = f[j] + f[j - i];
		}
	}
	cout << f[n];
	return 0;
}

另类方法

状态表示:f[i][j]表示能由j个数相加得到总和是i的组合个数

分成两种情况:(1)最小值为1则f[i-1][j-1]

                            (2) 最小者大于1则f[i-j][j],因为每个数都严格大于1所以对每个数拿出一个1来,j不变但是i变成i-j

状态方程:f[i][j] = f[i-1][j-1]+f[i-j][j]

一个正整数要想用正整数相加得到那么最多由全1相加,所以最后要的是sum是从1到n个数相加的组合数,因此sum=f[n][1]+f[n][2]+.....f[n][n]

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int f[N][N];//总数是i恰好表示成j个数的相加的个数

int main() {

	int n;
	cin >> n;
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			f[i][j] = f[i - 1][j - 1] + f[i - j][j];
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; i++) {
		sum += f[n][i];
	}
	cout << sum;
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值