1. 程式人生 > 其它 >P5194 [USACO05DEC]Scales

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;
}