[编程笔记]-Count_DP计数DP

概念

状态记录数量的DP。

对就是这么朴实无华。

例题

AcWing900-整数划分

方案一

基础版

首先设计状态:f[i][j]表示在从1~i的数字中能够组成j的组合数。

状态转移:根据选择几个(令为k)第i个数,可以直接转换:

f[i][j]+=f[i-1][j-k*i]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>
using namespace std;

const int N=1e3+3,mod=1e9+7;

int n;
int f[N][N];

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

优化版

还记得完全背包怎么优化不?

计数DP和完全背包算法是不是很像?

那就对了,所以我们就照着那个思想优化即可。

完全背包优化

f[i][j]=f[i-1][j]+f[i-1][j-i]+f[i-1][j-i2]+…+f[i-1][j-ik]
f[i][j-i]=f[i-1][j-1]+f[i-1][j-i2]+…+f[i-1][j-ik]
f[i][j-i2]=f[i-1][j-i2]+…+f[i-1][j-i*k]

所以就不用我多说了吧。

注:如果您光看优化版代码,如果您没有学过,如果您不是神童,您多半看不懂~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;

const int N=1e3+3,mod=1e9+7;

int n;
int f[N];

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

方案二

f[i][j]表示所有总和是i并恰好表示成j个数之和的方案个数。

状态转移时可以分成两种情况:方案中最小数是1的方案和方案中最小数大于1的方案。

它们分别是f[i-1][j-1]与f[i-j][j]

  • f[i-1][j-1]:把1去掉的情况
  • f[i-j][j]:给每一个数减去1的情况(神奇)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<bits/stdc++.h>
using namespace std;

const int N=1e3+3,mod=1e9+7;

int n;
int ans;
int f[N][N];

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

完结撒花o( ̄︶ ̄)o


[编程笔记]-Count_DP计数DP
http://githarlem.github.io/2024/08/09/Count-DP/
作者
Harlem
发布于
2024年8月9日
更新于
2024年8月9日
许可协议