题意:n个数字,只有既大于左边又大于右边的数字是山顶,要在山顶上插k面旗子,相邻旗子的距离>=k,最大化k
数据范围: n <= 50000
思路:最大化最小值,显然是二分,重点在如何写check函数
首先肯定是把所有山顶标记起来,设dp[i]为前i个点最多插多少旗子
然后:1.对于一个山顶 dp[i] = max(dp[i], dp[i-k]+1) 插旗子
2. 对于一个不是山顶 dp[i] = max(dp[i], dp[i-1]) 继承前面的值
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define LL long long 6 #define debug(x) cout << "[" << x << "]" << endl 7 using namespace std; 8 9 const int mx = 50010; 10 int dp[mx], n, a[mx]; 11 bool vis[mx]; 12 13 bool check(int k){ 14 memset(dp, 0, sizeof dp); 15 if (k == 1){ 16 for (int i = 1; i <= n; i++) 17 if (vis[i]) return 0; 18 return 1; 19 } 20 for (int i = 2; i <= n-1; i++){ 21 dp[i] = vis[i]; 22 if (!vis[i]) dp[i] = max(dp[i-1], dp[i]); 23 else if (i >= k) dp[i] = max(dp[i], dp[i-k]+1); 24 } 25 return dp[n-1] < k; 26 } 27 28 int main(){ 29 scanf("%d", &n); 30 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 31 for (int i = 2; i <= n-1; i++){ 32 if (a[i] > a[i-1] && a[i] > a[i+1]) 33 vis[i] = 1; 34 } 35 int l = 1, r = n; 36 while (l <= r){ 37 int mid = (l+r)>>1; 38 if (check(mid)) r = mid-1; 39 else l = mid+1; 40 } 41 printf("%d\n", r); 42 return 0; 43 }