1. 程式人生 > >POJ 3061

POJ 3061

 本題題意:

         有個長度為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;
}