[acwing]1023.小明买书
/*
dp[i][j] 表示只考虑前 i 个物品,其价值恰好为 j 的方案个数
dp[i][j] 可从选多少个第 i 个物品推导出来,假设最多能选 s 个
如果选 0 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * 0]
如果选 1 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * 1]
......
如果选 s 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * s]
综上,
dp[i][j] = dp[i - 1][j - vi * 0] + dp[i - 1][j - vi * 1] + ... + dp[i - 1][j - vi * s]
那么,
dp[i][j - vi] = dp[i - 1][j - vi * 1] + dp[i - 1][j - vi * 2] + ... + dp[i - 1][j - vi * s]
即,
dp[i][j] = dp[i - 1][j] + dp[i][j - vi]
遍历顺序应该是从上到下 i[1, 4],从左到右的 j[0, n]
初始化 dp[0][0] = 1
目标 dp[4][n]
*/
#include <cstdio>
using namespace std;
const int N = 1010;
int n;
int v[] = {-1, 10, 20, 50, 100};
int dp[5][N];
int main()
{
scanf("%d", &n);
dp[0][0] = 1;
for (int i = 1; i <= 4; i++)
for (int j = 0; j <= n; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= v[i]) dp[i][j] += dp[i][j - v[i]];
}
printf("%d", dp[4][n]);
return 0;
}
/*
空间优化
*/
#include <cstdio>
using namespace std;
const int N = 1010;
int n;
int v[] = {-1, 10, 20, 50, 100};
int dp[N];
int main()
{
scanf("%d", &n);
dp[0] = 1;
for (int i = 1; i <= 4; i++)
for (int j = 0; j <= n; j++) {
if (j >= v[i]) dp[j] += dp[j - v[i]];
}
printf("%d", dp[n]);
return 0;
}
[acwing]1050.鸣人的影分身
/*
dp[i][j][k] 表示只考虑前 i 个物品,其价值恰好为 j,且所选物品个数为 k 的方案的数量
dp[i][j][k] 可从选多少个第 i 个物品推导出来,假设最多能选 s 个
如果选 0 个第 i 个物品,dp[i][j][k] = dp[i - 1][j - vi * 0][k - 0]
如果选 1 个第 i 个物品,dp[i][j][k] = dp[i - 1][j - vi * 1][k - 1]
......
如果选 s 个第 i 个物品,dp[i][j][k] = dp[i - 1][j - vi * s][k - s]
综上,
dp[i][j][k] = dp[i - 1][j - vi * 0][k - 0] + dp[i - 1][j - vi * 1][k - 1] + ... + dp[i - 1][j - vi * s][k - s]
那么,
dp[i][j - vi][k - 1] = dp[i - 1][j - vi * 1][k - 1] + dp[i - 1][j - vi * 2][k - 2] + ... + dp[i - 1][j - vi * s][k - s]
即,
dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - vi][k - 1];
遍历顺序应该是 i[1, m + 1],j[0, m],k[0, n]
初始化 dp[0][0][0] = 1
目标 dp[m + 1][m][n]
*/
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 20;
int t, m, n;
int dp[N][N][N];
int main()
{
scanf("%d", &t);
while (t--) {
memset(dp, 0, sizeof(dp));
scanf("%d%d", &m, &n);
dp[0][0][0] = 1;
for (int i = 1; i <= m + 1; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k <= n; k++) {
int vi = i - 1;
dp[i][j][k] = dp[i - 1][j][k];
if (j - vi >= 0 && k - 1 >= 0) dp[i][j][k] += dp[i][j - vi][k - 1];
}
printf("%d\n", dp[m + 1][m][n]);
}
return 0;
}
/*
空间优化
*/
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 20;
int t, m, n;
int dp[N][N];
int main()
{
scanf("%d", &t);
while (t--) {
memset(dp, 0, sizeof(dp));
scanf("%d%d", &m, &n);
dp[0][0] = 1;
for (int i = 1; i <= m + 1; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k <= n; k++) {
int vi = i - 1;
if (j - vi >= 0 && k - 1 >= 0) dp[j][k] += dp[j - vi][k - 1];
}
printf("%d\n", dp[m][n]);
}
return 0;
}
[acwing]1226.包子凑数
/*
由贝祖定理知如果 n 个数的最大公因数不为 1,那么将有无数个数无法表示(因为只能表示 gcd 的倍数)
由[AcWing]1205.买不到的数目知两个数最大不能凑出来的数为 (n - 1) * (m - 1) - 1,如果多加一个数的话,
最大不能凑出来的数应该会变小,那么由数据范围可知其上界为 (99 - 1) * (100 - 1) - 1,这里直接取 10000
dp[i][j] 表示只考虑前 i 个物品,其价值总和是否恰好为 j
dp[i][j] 可从选多少个第 i 个物品推导出来,假设最多选 s 个
如果选 0 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * 0]
如果选 1 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * 1]
...
如果选 s 个第 i 个物品,dp[i][j] = dp[i - 1][j - vi * s]
综上,
dp[i][j] = dp[i - 1][j - vi * 0] | dp[i - 1][j - vi * 1] | ... | dp[i - 1][j - vi * s]
那么,
dp[i][j - vi] = dp[i - 1][j - vi * 1] | dp[i - 1][j - vi * 2] | ... | dp[i - 1][j - vi * s]
即,
dp[i][j] = dp[i - 1][j] | dp[i][j - vi]
遍历顺序 i[1, n], j[0, N)
初始化 dp[0][0] = 1
目标 dp[n][i] 如果为 0,表示无法表示
*/
#include <cstdio>
using namespace std;
const int N = 100010;
int n;
int v[110];
int dp[110][N];
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
scanf("%d", &n);
int d = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &v[i]);
d = gcd(d, v[i]);
}
if (d != 1) {
printf("INF");
return 0;
} else {
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < N; j++) {
dp[i][j] = dp[i - 1][j];
if (j - v[i] >= 0) dp[i][j] |= dp[i][j - v[i]];
}
}
int res = 0;
for (int i = 0; i < N; i++)
if (dp[n][i] == 0)
res++;
printf("%d", res);
return 0;
}
/*
空间优化
*/
#include <cstdio>
using namespace std;
const int N = 100010;
int n;
int v[110];
int dp[N];
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
scanf("%d", &n);
int d = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &v[i]);
d = gcd(d, v[i]);
}
if (d != 1) {
printf("INF");
return 0;
} else {
dp[0] = 1;
for (int i = 1; i <= n; i++)
for (int j = v[i]; j < N; j++)
dp[j] |= dp[j - v[i]];
}
int res = 0;
for (int i = 0; i < N; i++)
if (dp[i] == 0)
res++;
printf("%d", res);
return 0;
}