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 }