有n种大小不同的数字,各种各mi个,判断是否可以从这些数字之中选出若干个使他们的和恰好为K。
输入:
第一行为n
第二行为n种数字
第三行为这n中数的个数
第四行为K
输出:
可以 输出”yes”
不可以 输出“no”
样例:
3
3 3
5 2
8 2
17
法一:bool值判断不着重讲
#include<iostream>
#include <cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1010;
bool dp[maxn][maxn];
int a[maxn], m[maxn];
int n, K;
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
for (int i = 0; i < n; i++)
{
cin >> m[i];
}
memset(dp, false, sizeof(dp));
dp[0][0] = true;
cin >> K;
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= K; j++)
{
for (int k = 0; k*a[i] <= j || k <= m[i]; k++)//
{
dp[i + 1][j] |= dp[i][j - k*a[i]];//存在性问题,不断取或看看能否到dp[n][K].
}
}
}
if (dp[n][K])
cout << "YES" << endl;
else
cout << "No" << endl;
return 0;
}
法二:优化为一维数组:状态转移方程
dp[i+1][j] =
1.m[i](前i-1个数加和已经得到了K所以不需要i)(dp[i][j]>=0)
2.-1(两种情况1.加a[i]越界了大于k;
2如果在用前i个数能凑成和j-a[i-1].但m[i-1]的数目不够了
或无法用前i个数凑成和j - a[i-1])
(j<a[i]或dp[i+1][j-a[i]]<=0)
3.dp[i+1][j-a[i]]-1(其他情况)(前i格数加和可以得到j-a[i-1],所以只需数目减一即可。
代码:
#include<iostream>
#include <cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1010;
bool dp[maxn];
int a[maxn], m[maxn];
int n, K;
/* 状态转移方程:
dp[i+1][j] =
1.m[i](前i-1个数加和已经得到了K所以不需要i)(dp[i][j]>=0)
2.-1(两种情况1.加a[i]越界了大于k;
2如果在用前i个数能凑成和j-a[i-1].但m[i-1]的数目不够了
或无法用前i个数凑成和j - a[i-1])
(j<a[i]或dp[i+1][j-a[i]]<=0)
3.dp[i+1][j-a[i]]-1(其他情况)(前i格数加和可以得到j-a[i-1],所以只需数目减一即可。
*/
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
for (int i = 0; i < n; i++)
{
cin >> m[i];
}
cin >> K;
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= K; j++)
{
if (dp[j] >= 0)
{
dp[j] = m[i];
}
else if (j < a[i] || dp[j - a[i]]<=0)
{
dp[j] = -1;
}
else
{
dp[j] = dp[j - a[i]] - 1;
}
}
}
if (dp[K] >= 0)
{
cout << "YES" << endl;
}
else
{
cout << "No" << endl;
}
}