Acwing-基础算法课笔记之动态规划(计数类DP)

本文详细介绍了整数划分问题,探讨了完全背包的动态规划解法以及计数类DP的实现,包括过程模拟和闫氏DP分析,并提供了相应的C++代码示例。
摘要由CSDN通过智能技术生成

一、整数划分

1、定义

一个正整数 n可以表示成若干个正整数之和,形如:
n = n 1 + n 2 + . . . + n k n=n_1+n_2+...+n_k n=n1+n2+...+nk,其中 n 1 ≥ n 2 ≥ . . . ≥ n k , k ≥ 1 n_1\ge n_2\ge...\ge n_k,k\ge1 n1n2...nk,k1

举例:
假设要将 5 5 5划分,以下是划分的情况:
5 = 5 5=5 5=5
5 = 1 + 4 5=1+4 5=1+4
5 = 2 + 3 5=2+3 5=2+3
5 = 1 + 1 + 3 5=1+1+3 5=1+1+3
5 = 1 + 2 + 2 5=1+2+2 5=1+2+2
5 = 1 + 1 + 1 + 2 5=1+1+1+2 5=1+1+1+2
5 = 1 + 1 + 1 + 1 + 1 5=1+1+1+1+1 5=1+1+1+1+1

2、完全背包的做法

状态表示:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示只从 1 ∼ i 1\sim i 1i中选,且总和等于 j j j的方案数

状态转移方程:
f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − i ] f[i][j]=f[i-1][j]+f[i][j-i] f[i][j]=f[i1][j]+f[i][ji]
其中 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]指的是当物品装不进背包时,前 i − 1 i-1 i1个物品当中最大价值, f [ i ] [ j − i ] f[i][j-i] f[i][ji]指的是装的下的情况。(此为个人理解,如果有不对的地方请纠正错误)

代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int dp[N];
int mod = 1e9 + 7;
int main() {
	scanf("%d", &n);
	dp[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++) {
			dp[j] = (dp[j] + dp[j - i]) % mod;
		}
	}
	printf("%d", dp[n]);
	return 0;
}

(1)过程模拟

012345
0100000
1111111
2102233
3100345
4100056
5100007

上表中,列表示的是最终和的值,行表示和值最多由 j j j个数相加, d p [ i ] [ j ] dp[i][j] dp[i][j]则是表示的是方案个数。

(2)代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int dp[N];
int mod = 1e9 + 7;
int main() {
	scanf("%d", &n);
	dp[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++) {
			dp[j] = (dp[j] + dp[j - i]) % mod;
		}
	}
	printf("%d", dp[n]);
	return 0;
}

3、计数类DP的做法

(1)过程模拟

012345
0100000
1010000
2011000
3011100
4012110
5012211

上表中,列表示的是总和 i i i,行表示有 j j j个数相加, d p [ i ] [ j ] dp[i][j] dp[i][j]则是表示的是有 j j j个数相加构成 i i i的数量。

(2)闫氏DP分析法

在这里插入图片描述

(3)代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int dp[N][N];
int mod = 1e9 + 7;
int main() {
	scanf("%d", &n);
	dp[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			dp[i][j] = (dp[i - 1][j - 1] + dp[i - j][j]) % mod;
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)ans = (ans + dp[n][i]) % mod;
	printf("%d", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值