POJ--2018(二分,DP)

2014-12-24 01:05:35

思路:这题一开始无从下手....

  方法是二分平均值,检查能否达到。

  检查时,先将每个数减去所枚举的平均值,这样就把问题转化为求一段长度>=F,和大于等于零的子段是否存在的问题了。

  用sum[i]表示前i个数的和,考虑每i点的时候,要考虑i-F-1前面的点取或不取,转移方法:ans(i) = max{ ans(i-1) , sum[i] - sum[i - F] }

  这题貌似卡了精度...orz...

 PS:这题还有更神的做法,参见2004年周源的国家集训队论文,采用了数形结合、维护下凸折现的方法。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 const int INF = 1 << 30;
20 const int maxn = 100010;
21 const double eps = 1e-10;
22 
23 int N,F;
24 double num[maxn],sum[maxn];
25 
26 bool Check(double val){
27     double tmp = sum[F - 1] - val * (F - 1);
28     for(int i = F; i <= N; ++i){
29         tmp += num[i] - val;
30         tmp = max(tmp,sum[i] - sum[i - F] - val * F);
31         if(tmp > -eps) return true;
32     }
33     return false;
34 }
35 
36 int main(){
37     scanf("%d%d",&N,&F);
38     for(int i = 1; i <= N; ++i){
39         scanf("%lf",&num[i]);
40         sum[i] = sum[i - 1] + num[i];
41     }
42     double mid,l = 0.0,r = 2000.0;
43     while(fabs(r - l) >= eps){
44         mid = getmid(l,r);
45         if(Check(mid) == false) r = mid;
46         else l = mid;
47     }
48     printf("%d\n",(int)(r * 1000));
49     return 0;
50 }

 

posted @ 2014-12-24 01:13  Naturain  阅读(186)  评论(0编辑  收藏  举报