P5194 [USACO05DEC]Scales
这道题是一道搜索题,一种思路是直接对每一个数做出选与不选的判断,时间复杂度 \(O(2^n)\) 。在这种时间复杂度下,只能通过 \(n \leq 30\) 的数据。
如何进行优化呢?以下是我的优化:
- 改变搜索顺序。这一道题的输入数据是一个不下降序列,如果我们把小的数放在前面,而 \(C\) 又比较大的话,前面的小数就会有很多的空间进行选择,极限数据下甚至可以卡死代码。为了避免这种情况,我在读入的时候从 \(a_n\) 开始倒着读,这样 a 数组中就是一个不上升子序列,前面的大数很容易就因为 \(C\) 的限制失去很多选择,节省了很多的时间。其中 a 数组是我存放数的数组。
- 模拟可行性剪枝,我们不妨这么想:如果说当前所选的数的总和加上后面的数的总和,即后缀和都没有超过 \(C\) 的话,那么当前的和就是在这种选择下可以达到的最大值。既然我们已经知道了最大值,并且题目所求的就是最大值,此时我们可以直接去更新答案, 然后退出这一层搜索。面对数很多的时候,这个剪枝会发挥出极大的威力。
在这些优化下,搜索的复杂度会变得很低。当然,如果您有什么更好的优化方法或剪枝,请私信我,感谢!
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000+10;
int n,c,a[MAXN];//n c 如题 a 存放数
typedef long long LL;
LL sum[MAXN],ans;//sum 后缀和 ans 答案
LL Max(LL fir,LL sec)
{
return (fir>sec)?fir:sec;
}
void dfs(int k,int now)
{
if(k>n)//选完了
{
ans=Max((LL)now,ans);
return;
}
if(now+sum[k]<=c)//优化2
{
ans=Max(now+sum[k],ans);
return;
}
if(now+a[k]<=c) dfs(k+1,now+a[k]);
dfs(k+1,now);
}
int main()
{
scanf("%d %d",&n,&c);
for(int i=n;i>=1;i--) scanf("%d",&a[i]);//优化1
for(int i=n;i>=1;i--) sum[i]=sum[i+1]+a[i];
dfs(1,0);
printf("%lld\n",ans);
return 0;
}