時間限制: 10000msios
單點時限: 1000ms算法
內存限制: 256MBless
一隻木桶能盛多少水,並不取決於桶壁上最高的那塊木板,而偏偏取決於桶壁上最短的那塊。函數
已知一個木桶的桶壁由N塊木板組成,第i塊木板的長度爲Ai。工具
如今小Hi有一個快捷修補工具,每次可使用修補工具將連續的不超過L塊木板提升至任意高度。優化
已知修補工具一共可使用M次(M*L<N),如何修補才能使最短的那塊木板最高呢?spa
注意: 木板是環形排列的,第N-1塊、第N塊和第1塊也被視爲連續的。內存
第1行:3個正整數,N, M, L。分別表示木板數量,修補工具使用次數,修補工具每次能夠同時修補的木板數。 1≤N≤1,000,1≤L≤20,M*L<Nstring
第2行:N個正整數,依次表示每一塊木板的高度Ai,1≤Ai≤100,000,000it
第1行:1個整數。表示使用修補工具後,最短木塊的所能達到的最高高度
第一個修補工具覆蓋[2 3 4]
第二個修補工具覆蓋[5 8 1]
樣例輸入
8 2 38 1 9 2 3 4 7 5
樣例輸出
7
本題可使用二分答案的思路解決。
咱們考慮這樣一個問題,假設最終最短的木板長度至少是K,最小須要使用修復工具幾回? 爲了描述方便咱們將這個最少次數記做F(K)。
因而咱們的問題變成求出最大的K,知足F(K) <= M。
若是咱們將F(K)當作一個函數,隨着K增長,咱們要修復的木板愈來愈多,顯然F(K)也會愈來愈大。
換句話說F(K)是單調遞增的。咱們能夠用二分來求出最大的K。
考慮 1 <= Ai <= 100000000,答案也必定在[1, 100000000]之間。在這個範圍內二分的複雜度是O(log(Max{Ai}))。
而後咱們的問題是對於肯定的K,計算F(K)。
當K肯定是,咱們就能夠肯定哪些木板須要被修復(Ai < K的木板)。
因爲木桶是環形的,咱們須要枚舉起點,複雜度O(N)。
一旦起點肯定,就能夠貪心求出每一次修復的位置。從而計算出F(K),複雜度O(N)。
因而總複雜度是O(log(Max{Ai})N^2)
這個算法能夠經過全部的數據。
不過上面算法中二分和計算F(K)都有優化的空間。
對於二分答案這部分,實際上答案必定是某個Ai,因此咱們能夠優化到O(logN)的二分。
對於計算F(K)的部分,考慮到修復範圍是L,因此O(N)的枚舉起點能夠優化到O(L)。
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;int n,m,l,a[2*1005],b[1005],rr,ll,mid;bool is_ok() { if(b[0]==mid) return true; //時間複雜度爲l*n int cnt=0;//須要的段數 int cnt_start=0;//已開始的起點數 bool f1=false; for(int i=0; i<n; ++i) { int cnt=1,j,last; if(a[i]<mid) { last=i+n; j=i+l; ++cnt_start; } else continue; while(j<last) { if(a[j]<mid) { ++cnt; j+=l; } else ++j; } if(cnt<=m) { f1=true; break; } if(cnt_start>=l) break; } return f1;}int main() { scanf("%d%d%d",&n,&m,&l); for(int i=0; i<n; ++i) scanf("%d",a+i); memcpy(b,a,n*sizeof(int)); memcpy(a+n,a,n*sizeof(int)); sort(b,b+n,less<int>()); ll=0,rr=n-1; while(ll<rr) { int t=(ll+rr+1)/2;//這個+1要注意 mid=b[t]; if(is_ok()) { ll=t; } else { rr=t-1;//這個地方要注意下 } } cout<<b[ll]<<endl; return 0;}