51nod1007(01背包)
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1007
题意:中文题诶~
思路:尽量将一个数组分成两个相等的部分,就是从原数组中选出一些元素使其和尽量接近所有元素的和的一半啦.至于如何选元素,我们可以用01背包解决;
假设原数组所有元素和为sum,那么可以分为 ans1 >= sum/2 和 ans2 <= sum/2 两部分,为了方便我们求 ans2 <=sum/2那部分, 那么此题的答案即为 sum-2*ans2,
接下来我们只要考虑如何用01背包求ans2就好啦;
我们用 dp[i][j] 表示到地 i 个元素为止,选择的元素和小于等于 j 的最大值,那么有 ans2=dp[n][num/2];
状态转移方程式为:
dp[i][j] = max (dp[i-1][j], dp[i-1][j-a[i]]+a[i]) 前者为不选当前元素,后者为选当前元素.注意后者状态的转变,最接近 j-a[i] 的那个数加上 a[i] 自然就是最接近 j 的数啦.
代码:
#include <bits/stdc++.h>
#define MAXN 110
using namespace std;
int a[MAXN], dp[MAXN][MAXN*MAXN]; //dp[i][j]存储的到地i个元素为止,可以得到的小于等于j的最大值
int main(void){
int n, sum=0;
cin >> n;
for(int i=1; i<=n; i++){
cin >> a[i];
sum+=a[i];
}
for(int i=1; i<=n; i++){
for(int j=sum/2; j>=a[i]; j--){
dp[i][j]=max(dp[i-1][j], dp[i-1][j-a[i]]+a[i]);//选或不选a[i]
}
}
cout << sum-2*dp[n][sum/2] << endl;
return 0;
}
上述算法的时间复杂度和空间复杂度分别为:O(sum*n), O(sum*n);
我们可以进一步优化一下空间复杂度,通过上面的代码我们不难发现 dp[i][j] 都是由 dp[i-1][j] 得到的,也就是我们是直接由前一步的答案得到后一步结果的,直至得到最终答案.那么我们可以不存储前面的数据.用 dp[j]存储当前能得到的小于等于 j 的最大值,然后逐步更新dp[j]的值就可以得到答案啦;
其空间复杂度为 O(sum)
代码:
1 #include <bits/stdc++.h>
2 #define MAXN 110
3 using namespace std;
4
5 int a[MAXN], dp[MAXN*MAXN]; //dp[j]存储可以得到的小于等于j的最大值
6
7 int main(void){
8 int n, sum=0;
9 cin >> n;
10 for(int i=0; i<n; i++){
11 cin >> a[i];
12 sum+=a[i];
13 }
14 for(int i=0; i<n; i++){ //外循环通过选中a[i]来更新dp[j]的值
15 for(int j=sum/2; j>=a[i]; j--){
16 dp[j]=max(dp[j], dp[j-a[i]]+a[i]);
17 }
18 }
19 cout << sum-2*dp[sum/2] << endl;
20 return 0;
21 }
我就是我,颜色不一样的烟火 --- geloutingyu