UVa 1451:Average(數形結合)
阿新 • • 發佈:2019-02-16
題意:給定一個長度為n的01串,選一個長度至少為L的連續子串,使得子串中數字的平均值最大。如果有多解,字串長度應儘量小;如果仍有多解,起點編號儘量小。序列中的字元編號為1~n,因此
[1,n] 就是完整的字串。1≤n≤100000,1≤L≤1000 。例如:對於如下長度為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;
}