P5194 [USACO05DEC]Scales

这道题是一道搜索题,一种思路是直接对每一个数做出选与不选的判断,时间复杂度 \(O(2^n)\) 。在这种时间复杂度下,只能通过 \(n \leq 30\) 的数据。

如何进行优化呢?以下是我的优化:

  1. 改变搜索顺序。这一道题的输入数据是一个不下降序列,如果我们把小的数放在前面,而 \(C\) 又比较大的话,前面的小数就会有很多的空间进行选择,极限数据下甚至可以卡死代码。为了避免这种情况,我在读入的时候从 \(a_n\) 开始倒着读,这样 a 数组中就是一个不上升子序列,前面的大数很容易就因为 \(C\) 的限制失去很多选择,节省了很多的时间。其中 a 数组是我存放数的数组。
  2. 模拟可行性剪枝,我们不妨这么想:如果说当前所选的数的总和加上后面的数的总和,即后缀和都没有超过 \(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;
}
posted @ 2022-04-12 21:28  Plozia  阅读(37)  评论(0编辑  收藏  举报