1. 程式人生 > >UVa 1451:Average(數形結合)

UVa 1451:Average(數形結合)

題意:給定一個長度為n的01串,選一個長度至少為L的連續子串,使得子串中數字的平均值最大。如果有多解,字串長度應儘量小;如果仍有多解,起點編號儘量小。序列中的字元編號為1~n,因此[1,n]就是完整的字串。1n100000,1L1000。例如:對於如下長度為17的序列001010111011011010,如果L=7,最大平均值為6/8(子序列為[7,14],其長度為8);如果L=5,子序列[7,11]的平均值最大,為4/5。(本段摘自《演算法競賽入門經典(第2版)》)

分析:
       利用數形結合的思想,將求平均值轉化成求斜率,利用棧維護下凸曲線,則最大值存在於下凸曲線中。具體分析請見“淺談數形結合思想在資訊學競賽中的應用”。

程式碼:

#include <fstream>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <sstream>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <set>
#include <string>
#include <vector> using namespace std; const int maxn = 100000 + 5; int T, n, l, i, j, L, R, tmp; char s[maxn]; int sum[maxn], p[maxn]; int cmp(int x1, int x2, int x3, int x4) { return (sum[x2] - sum[x1 - 1]) * (x4 - x3 + 1) - (sum[x4] - sum[x3 - 1]) * (x2 - x1 + 1); } int main() { scanf
("%d", &T); for (int C = 0; C < T; ++C) { scanf("%d%d%s", &n, &l, s + 1); for (int k = 1; k <= n; ++k) sum[k] = sum[k - 1] + s[k] - '0'; i = 0; j = 0; L = 1; R = l; for (int k = l; k <= n; ++k) { while (j - i > 1 && cmp(p[j - 2], k - l, p[j - 1], k - l) >= 0) --j; p[j++] = k - l + 1; while (j - i > 1 && cmp(p[i], k, p[i + 1], k) <= 0) ++i; tmp = cmp(p[i], k, L, R); if (tmp > 0 || (tmp == 0 && R - L > k - p[i])) { L = p[i]; R = k; } } printf("%d %d\n", L, R); } return 0; }