明显的多重背包
若按普通写法的话复杂度Onmk一定超时
仔细想想
普通写法的话
dp[i][j]表示的是 前 i 件硬币 是否鞥凑成 j 的面值
即存的是1或0
这样会浪费好多信息
其实让dp[i][j][表示前i件硬币凑成j然后第i件硬币的剩余数
所以
当前i-1件硬币能够凑j时第i种硬币直接不用全剩下
即dp[i][j] = coinNum[i];
否则当满足条件时dp[i][j] = dp[i][j - coin[i]] - 1;
其他情况为-1表示不可能
优化的核心在第二步
省去了k的遍历
所以把复杂度降为Onm
一开始写的没滚动数组优化:
但更好理解上面的思想
ME了
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
int coin[110] = {0};
int cNum[110] = {0};
int dp[110][100010] = {0};
int read()
{
int x = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') w = -1;
ch = getchar();
}
while(ch <= '9' && ch >= '0')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * w;
}
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
if(n == 0 && m == 0)
{
break;
}
int ans = 0;
for(int i = 1; i <= n; i++)
{
coin[i] = read();
}
for(int i = 1; i <= n; i++)
{
cNum[i] = read();
}
for(int i = 1; i <= m; i++)
{
dp[0][i] = -1;
}
dp[0][0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
if(dp[i - 1][j] >= 0)
{
dp[i][j] = cNum[i];
continue;
}
if(j - coin[i] >= 0 && dp[i][j - coin[i]] > 0)
{
dp[i][j] = dp[i][j - coin[i]] - 1;
}
else
{
dp[i][j] = -1;
}
}
}
for(int i = 1; i <= m; i++)
{
if(dp[n][i] >= 0)
{
ans++;
}
}
cout << ans << endl;
}
return 0;
}
滚动数组优化:
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
int coin[110] = {0};
int cNum[110] = {0};
int dp[100010] = {0};
int read()
{
int x = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') w = -1;
ch = getchar();
}
while(ch <= '9' && ch >= '0')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * w;
}
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
if(n == 0 && m == 0)
{
break;
}
int ans = 0;
for(int i = 1; i <= n; i++)
{
coin[i] = read();
}
for(int i = 1; i <= n; i++)
{
cNum[i] = read();
}
for(int i = 1; i <= m; i++)
{
dp[i] = -1;
}
dp[0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
if(dp[j] >= 0)
{
dp[j] = cNum[i];
continue;
}
if(j - coin[i] >= 0 && dp[j - coin[i]] > 0)
{
dp[j] = dp[j - coin[i]] - 1;
}
else
{
dp[j] = -1;
}
}
}
for(int i = 1; i <= m; i++)
{
if(dp[i] >= 0)
{
ans++;
}
}
cout << ans << endl;
}
return 0;
}
其实复杂度还很高
还可以进一步优化
转化01背包或者单调队列优化
见添加链接描述