51nod 1281 二分+DP

题意: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 }

 

posted @ 2018-09-10 16:59  QAQorz  阅读(148)  评论(0编辑  收藏  举报