题解 | #数组分组# 01背包+处理负数越界
数组分组
https://www.nowcoder.com/practice/9af744a3517440508dbeb297020aca86
看了取值和数组大小,是可以用01背包做的。讲的人比较少,那我把自己的ac代码分享一下。
思路:
1)考虑把3和5的倍数分别求和(sum_3和sum_5)之后,剩下部分的元素(arro)求和(sum_left)// 时间复杂度o(n);
剩下的问题是:要从arro中挑选一部分元素出来放入5的数组中,它们的和为x。根据题意可以得到:
sum_5+x=sum_3+sum_left-x;
== > tar=(sum_3+sum_left-sum_5) / 2;这就是背包问题的目标和
然后解决一些特殊情况(line26-32)
2)转化为01背包问题 // 时间复杂度o(mk),m是子数组大小,k=sum_max;
这里做法和01是一样的,只需要解决负数越界的问题。
越界有向前和向后两种,向前的需要提前计算一个偏移量offset,向后计算一个最大值sum_max;
然后注意初始化和最后返回答案时,恢复偏移量就行,中间过程和01背包一致。
btw,由于存在负数,不能用倒序遍历来优化空间复杂度的方法。
#include<iostream> #include<vector> #include<algorithm> #include<math.h> using namespace std; int n; int arr[50]; bool func(){ vector<int> arro; int sum_5=0, sum_3=0, sum_left=0; // sum_left:非3和5的倍数的数之和 int sum_max=0, offset=0; // 考虑负数导致的越界,计算前后的偏移量 for(int i=0;i<n;i++){ int x=arr[i]; if(x%5==0) sum_5+=x; else if(x%3==0) sum_3+=x; else { sum_left+=x; sum_max+=abs(x); offset+=x>=0? 0: abs(x); arro.push_back(x); } } int tar=(sum_3+sum_left-sum_5)/2; if(2*tar!=sum_3+sum_left-sum_5) return false; if(arro.size()==0){ return tar==0; }else if(arro.size()==1){ return tar==0||tar==arro[0]; } // 01背包部分,对数组的和加了偏移量offset int m=arro.size(); vector<vector<int>> d(m+1, vector<int>(sum_max+1, 0)); d[0][offset]=true; for(int i=1;i<=m;i++){ int x=arro[i-1]; for(int j=sum_max;j>=0;j--){ d[i][j]=d[i-1][j]; if(j-x>sum_max||j-x<0) continue; d[i][j]=d[i][j]||d[i-1][j-x]; } } return d[m][tar+offset]; } int main(){ cin>>n; for(int i=0;i<n;i++){ cin>>arr[i]; } if(func()) cout<<"true\n"; else cout<<"false\n"; return 0; }