计数类dp
整数划分:
给出1个数n,使它划分成a1,a2,a3,…,an的形式。其中a1≥a2≥a3≥…≥an。
方法一:完全背包
用其中有k个i的组合方式,把这k个i去掉,组成总和为j - k * i。
可以推出状态方程为:
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + … + f[i - 1][j - k * i];
f[i][j - i] = f[i - 1][j - i] + … + f[i - 1][j - k * i];
得 f[i][j] = f[i - 1][j] + f[i][j - i];
转换成一维:f[j] = f[j] + f[j - i];
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
int f[N];
int n;
int main()
{
cin >> n;
f[0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
f[j] = (f[j] + f[j - i]) % mod;
cout << f[n] << endl;
return 0;
}
方法二:计数dp
将问题分成最小值为1和最小值大于1的模式。i代表总和,j代表分成几组。
推转移方程:
(1)如果最小值是1时,去掉这个最小值。则可以表示为f[i - 1][j - 1];
(2)如果最小值大于1时,将每组的值减1。则可以表示为f[i - j][j];
f[i][j] = f[i - 1][j - 1] + f[i - j][j];
ans = f[n, 1] + f[n, 2] + f[n, 3] + … + f[n, n];
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
int f[N][N];
int n;
int main()
{
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;
int res = 0;
for(int i = 1; i <= n; i ++)
res = (res + f[n][i]) % mod;
cout << res << endl;
return 0;
}
数位统计dp
求某个区间中各个数字出现的次数,例如:11 , 12, 13, 14, 15。数字1出现的次数是6次。
count(n, x)表示1~n中x出现的次数。
如果想表示[a, b]中x出现的次数,只需用count(b, x) - count(a - 1, x);
分情况讨论:
1~n, x.
n = abcdefg
分别求x在各位上的个数。
当x > 0时
- 000~abc - 1, x, 000 ~ 999 有abc * 1000种情况;
- abc, x
1)当d < x 情况为0;
2)当d = x 000 ~ efg 有efg种情况;
3)当d > x 000 ~ 999 有1000种情况。
当x = 0时 - 001~abc - 1, x, 000 ~ 999 有abc * 1000种情况;
- abc, x
1)当d < x 情况为0;
2)当d = x 000 ~ efg 有efg种情况;
3)当d > x 000 ~ 999 有1000种情况。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
int get(vector<int> num, int l, int r)
{
int res = 0;
for(int i = l; i >= r; i --)
res = res * 10 + num[i];
return res;
}
int power10(int x)
{
int res = 1;
while(x --) res *= 10;
return res;
}
int count(int n, int x)
{
if(!n) return 0;
vector<int> num;
while(n)
{
num.push_back(n % 10);
n /= 10;
}
n = num.size();
int res = 0;
for(int i = n - 1 - !x; i >= 0; i --)
{
if(i < n - 1)
{
res += get(num, n - 1, i + 1) * power10(i);
if(!x) res -= power10(i);
}
if(num[i] == x) res += get(num, i - 1, 0) + 1;
else if(num[i] > x) res += power10(i);
}
return res;
}
int main()
{
int a, b;
while(cin >> a >> b, a || b)
{
if(a > b) swap(a, b);
for(int i = 0; i < 10; i ++)
cout << count(b, i) - count(a - 1, i) << ' ';
cout << endl;
}
return 0;
}