P7635 [COCI2010-2011#5] DVONIZ
阿新 • • 發佈:2021-06-27
演算法分析:尺取
看到本題,有些同學可能會想到二分。但實際上,答案並不滿足單調性。為了敘述方便,以下稱區間 \(i \to i+k-1\) 為左區間,區間 \(i+k \to i+2\times k-1\) 為右區間。在 \(k\) 增大時,左區間的值的確滿足單調性,但右區間的值有減有增,不滿足單調性。具體看以下樣例:
6 10
1 6 3 8 1 1
當 \(i=1 ,k=2\),時,右區間和大於 \(s\),不滿足條件,但當 \(k\) 取 \(k=3\) 時,滿足條件。
由此可見,答案並不滿足單調性。
根據題意從左往右依次計算的時間複雜度為 \(\mathcal{O}(n^2)\)
在第 \(i\) 次迴圈開始前,\(i+1 \to r\) 算得的 \(r\) 是當前可能的最大右邊界。即,在整個過程中,\(r\) 是單調不增的。
初始化:
在 \(i=n\) 時,顯然 \(k=0\) 是當前的最優解。
保持:
在 \(i+1\to i\) 時,存在兩種情況。
- \(i\to r\) 仍符合條件,此時滿足 \(sum_r-sum_{l-1}\le s\) 且 \(sum_{r+k}-sum_r\le s\)。若在情況 \(i+1\) 時,\(r\) 受到 \(r+k\le n\) 的限制而不能增大,那麼在當前狀態下,\(r\) 同樣受此限制。若 \(r+k<n\),假設 \(r\) 能增大,那麼 \(k\) 也隨之增大,於是左區間的和在前一次的基礎上繼續增大。在 \(r+k<n\) 的情況下,根據迴圈不變式,\(k\) 必定已經取到極限值,即左區間不能繼續向右擴張。這與 \(k\)
增大矛盾,因此 \(r\) 同樣不會增大,迴圈不變式得到保持。- \(i\to r\) 不符合條件。那麼我們將 \(r\) 左移,左、右區間均隨之減小,且必定一個存在 \(r\) 使得 \(sum_r-sum_{l-1}\le s\) 且 \(sum_{r+k}-sum_r\le s\)。且此時左區間恰滿足條件,此時滿足迴圈不變式。
終止:
顯然,$i \ge 1 $ ,在 \(i=1\) 之後,過程終止。
綜上所述,迴圈不變式得到證明,演算法正確性得到保證。同時,在演算法中, \(r\) 隨 \(i\) 的變化而單調不增,因此時間複雜度為 \(\mathcal{O}(n)\)。
下面給出具體的程式碼:
#include<bits/stdc++.h>
#define reg register
#define ll long long
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline int read();
const int N=1e5+1;
int n,s,sum[N],ans[N];
int main() {
n=read(),s=read();
F(i,1,n)sum[i]=sum[i-1]+read();//字首和
reg int r=n,k;
for(reg int i=n; i; --i) {//從右往左計算
while(r>=i) {
k=r-i+1;//r為左區間的右端點。
if(r+k<=n and sum[r]-sum[i-1]<=s and sum[r+k]-sum[r]<=s)break;
--r;
}
ans[i]=r-i+1<<1;//計算出可行的長度。
}
F(i,1,n)printf("%d\n",ans[i]);
return 0;
}
inline int read() {
reg int x=0;
reg char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
歡迎交流討論,請點個贊哦~