题目大意
解题思路
- 对于每个数,我们有两种选择
- (1): 选
- (2): 不选
- (1): 这道题的背包思想:
- 定义状态: d p [ i ] [ j ] dp[i][j] dp[i][j]: 前 i i i个数字里是否能够选出和为 j j j的数字,若能,则为1,不能则为0。
- 目标态: d p [ n ] [ k ] dp[n][k] dp[n][k].
- 状态转移: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∣ ∣ d p [ i − 1 ] [ j − a [ i ] ] dp[i][j] =dp[i-1][j] \quad|| \quad dp[i-1][j-a[i]] dp[i][j]=dp[i−1][j]∣∣dp[i−1][j−a[i]]. 其中前一项对应不选该数,后一项对应选该数。
- 更新策略: i i i从小到大更新。
- 初始态: d p [ 0 ] [ ∗ ] = 0 , d p [ 0 ] [ 0 ] = 1 dp[0][*] = 0, dp[0][0] = 1 dp[0][∗]=0,dp[0][0]=1: 表示没有数时只能和为0.
- 复杂度: O ( n 2 a i ) O(n^2a_i) O(n2ai)
- 注意点: 因为有负数,因此需要加入一个偏移量
offset
。
- (2): 这道题的深度优先搜索思想:
- 对每个物品,选或者不选,生成深度优先搜索树进行深搜。
- 当搜到和为k的节点时,置全局标志位
flag
为1。 - 深搜递归出口:
- 已经没有数可供选择时。
- 当前节点代表的和已经等于k。
- 若标志位为1,则不再继续搜。
- 复杂度:
2
n
2^n
2n.
- 比较,很明显,对这道题,深度优先搜索更占优势。
代码
- 背包内存溢出,按 a i a_i ai最大值绝对值为 1000 1000 1000写代码:
#include<iostream>
#include<cstring>
using namespace std;
const int offset = 1000;
const int MAXN = 21;
const int MAXM = MAXN * 2 * offset + 5;
int dp[2][MAXM];
int a[MAXN];
int main()
{
int n, k;
int min_sum;
int max_sum;
while(cin >> n)
{
min_sum = 0;
max_sum = 0;
for(int i=1; i<=n; i++)
{
cin >> a[i];
if(a[i] < 0)
min_sum += a[i];
else
max_sum += a[i];
}
cin >> k;
memset(dp, 0, sizeof(dp));
dp[0][0+offset] = 1;
for(int i=1; i<=n; i++)
{
for(int j=-min_sum; j<=max_sum; j++)
{
// 小于-min_sum 和 大于max_sum的情况是不可能存在的,因此只能选择不选。
if(j-a[i] < -min_sum || j-a[i] > max_sum)
dp[i%2][j+offset] = dp[(i-1)%2][j+offset];
else
dp[i%2][j+offset] = dp[(i-1)%2][j+offset] || dp[(i-1)%2][j+offset-a[i]];
}
}
if(dp[n%2][k+offset])
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
- 深度优先搜索代码
#include<iostream>
using namespace std;
const int MAXM = 21;
int a[MAXM];
int sum;
int k;
int n;
int flag;
void dfs(int deep)
{
// 已经无数可选了
if(deep == n)
{
flag = (sum == k);
return;
}
// 该节点和等于k
if(sum == k)
{
flag = 1;
return;
}
// 不选该数
dfs(deep+1);
if(flag)
return
// 选该数
sum += a[deep];
dfs(deep+1);
if(flag)
return;
sum -= a[deep];
return;
}
int main()
{
while(cin >> n)
{
for(int i=0; i<n; i++)
cin >> a[i];
cin >> k;
sum = 0;
flag = 0;
dfs(0);
if(flag)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}