励志用少的代码做高效表达
题意
给定N个物品的中联L1,背包的容量M,同时要求每个背包最多装两个物品,求至少要多少个背包才能装下所有的物品。
解题过程
第一次接触背包类问题。
最初的思路是降序排序,i从最大值遍历,j从i后遍历,直到找到可以装在一个背包里的两个物体,若无,则把最大值单独装包。时间复杂度为O(n^2),超时且复杂。
网搜后,发现更优化的思路是:升序排序,从i最小的值遍历,j从最大的值遍历,若二者和满足条件,则装包, 反之将较大值单独装包。
于是我陷入了思考, 为什么只是将排序调整一下,时间复杂度和代码复杂度大不相同呢?
经过脑暴,我得到了以下结论:
如果降序排序,每遍历一次,不符合条件者会被跳过,进而参与一次又一次的循环判断;
而升序排序后,每遍历一次,无论是否符合条件,都会被处理,因此效率大大提高(简单二分的原理,分而治之)。
也正因后者每次遍历都会做相应的处理,因此每次判断都会控制在数列的两端,思路就会简单很多。
可见,一道题的AC与否往往在一念之间,如果灵活一些思考,或许会更快更好的解决问题。
下面贴代码。
需要注意的是,虽然N<=10^5, 但数组设为a[100005]会爆掉。 最后改成了a[100010],成功AC。也算是一个小技巧。
#include<bits/stdc++.h>
using namespace std;
int a[100010];
int main() {
ios::sync_with_stdio(false);
int T; cin>>T; while(T--) {
int n, num; cin>>n>>num;
for(int i = 0; i < n; i++) cin>>a[i];
sort(a, a+n);
int i = 0, j = n-1, sum = 0;
while(i <= j) {
if(i == j) {
sum++; break; }
if(a[i]+a[j] <= num) {
i++; j--; sum++; }
else {
j--; sum++; }
}
cout << sum << endl << (T?"\n":"");
}
return 0; }