POJ 3061
阿新 • • 發佈:2018-12-18
本題題意:
有個長度為n的陣列,問是否存在區間能讓區間內部數字和大於k (要求區間儘可能短)
第一行輸入 n k
之後輸入陣列
最終輸出最短的區間長度
這道題我們可以用二分法解決,時間大概是 O(nlogn)即一次全部遍歷和一次二分搜尋
有了之前幾道題的竟然。。這道解決的也更加快了建議先自己想一下
程式碼如下
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int a[1e8 + 10]; int n, s; void slove(){ int sum = 0; for (int i = 0; i < n; i++) { //記錄一個字首和 sum += a[i]; a[i] = sum; } int res = n + 1; for (int i = 0; a[i] + s <= a[n - 1]; i++) { int tmp = lower_bound(a + i, a + n, a[i] + s) - a - i; //呼叫函式 // lower_bound 是一個封裝好的函式,會返回裡搜尋值最近的較大的數 res = min(res, tmp); } if (res == n + 1)printf("0\n"); else printf("%d\n", res); } int main() { int t; scanf("%d", &t); while (t--) { memset(a, 0, sizeof(a)); scanf("%d%d", &n, &s); for (int i = 0; i < n; i++) scanf("%d", a[i] ); slove(); } return 0; }
當然 還有一種更加優秀的演算法 我們叫它 尺取法(因為這種演算法很像一種蟲子蠕動的感覺,貌似因為尺蠖這種蟲子音譯的原因)
想象我們構造一個滑動區間(類似於一個可以推動的玻璃門)這個區間內的數字之後如果大於 s 那麼這個區間就是成立的然後記錄區間長度並且比較。至於怎麼去確保這個區間滑動同時裡邊數字和大於 s 我們就採用那種蟲子蠕動的思想:
上邊界持續增長 ,直到大於S
下邊界開始收縮 ,直到小於S
最後記錄 每個 Up-Down 的值 比較出最大 就是這種思想了。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; int s,e; int t,n,k; const int maxn = 1e8+5; int num[maxn]; int main() { cin>>t; while(t--) { cin>>n>>k; for(int i=1;i<=n;i++) { cin>>num[i]; } s=e=1; int sum=0; int res=n+1; for(;;)//構造一個死迴圈 { while(e<=n&&sum<k)//開始把上邊界增長 //就像蟲子蠕動時頭先向前伸出去 { sum+=num[e]; e++; } if(sum<k)break;//如果走到了頭就結束 res=min(res,e-s);//記錄最短區間 sum-=num[s++];//然後增長下邊界 //像蟲子蠕動的時候尾部再向回收縮 } if(res>n)res=0; cout<<res<<endl; } return 0; }