题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2993
题目大意:给定一个长度为n的序列,从其中找连续的长度大于m的子序列使得子序列中的平均值最小。
解题思路:经典斜率优化DP,04年周维的论文《浅谈数形结合思想在信息学竞赛中的应用》有很详细的分析,这里只讲下实现。
本题设子序列长度为x,子序列内和为y,使用单调队列来维护凸包的凸性。每次遍历到新的点时都要先把队尾的凸点给去掉,判断条件是y1 * x2 <= y2 * x1((x1,y1)是队尾的点,(x2,y2)是队尾的前一个点). 然后每次更新答案的时候,要把不可能是最优解的点给去掉,判断条件是y1 * x2 <= y2 * x1((x1,y1)是队头的点,(x2,y2)是队头的后一个点)。
我的第一次ac代码700+ms,第二次加了输入外挂就成312ms了,然后将sum数组从double改成int成281ms,Rank3.
测试数据:
Input:
C艹代码:
#include <stdio.h> #include <string.h> #define MAX 100001 double ans; int sum[MAX],n,len; int qu[MAX],head,tail; double Solve_DP() { int i,j,k,pre,cur; double x1,x2,y1,y2; ans = 0; qu[tail] = 0; head = tail = 0; for (i = len; i <= n; ++i) { cur = i - len; while (head < tail) { //维护队列内凸包的凸性 pre = qu[tail]; x1 = cur - pre; y1 = sum[cur] - sum[pre]; pre = qu[tail-1]; x2 = cur - pre; y2 = sum[cur] - sum[pre]; if (y1 * x2 <= y2 * x1) tail--; else break; } qu[++tail] = cur; while (head < tail) { //寻找最优的那个点 cur = i; pre = qu[head]; x1 = cur - pre; y1 = sum[cur] - sum[pre]; pre = qu[head+1]; x2 = cur - pre; y2 = sum[cur] - sum[pre]; if (y1 * x2 <= y2 * x1) head++; else break; } pre = qu[head]; double temp = (sum[i] - sum[pre]) * 1.0 / (i - pre); if (temp > ans) ans = temp; } return ans; } int Input() { char ch = ' '; while (ch < '0' || ch > '9') ch = getchar(); int x = 0; while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0',ch = getchar(); return x; } int main() { int i,j,k; while (scanf("%d%d",&n,&len) != EOF) { for (i = 1; i <= n; ++i) k = Input(),sum[i] = sum[i-1] + k; ans = Solve_DP(); printf("%.2lf\n",ans); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。