【题解】UVA1451 平均值 Average
一道很有意思的题目
题目大意:
给定一个由 0 和 1 组成的序列,求出一个子序列使其数字的平均值最大,并输出方案。
解题思路:
看到数据,立刻想到 \(O(n\log n)\) 的时间复杂度,
立马联想到二分。
那么以什么为关键值二分呢?左端点?右端点?区间长度?
平均值。
没错,这道题可以用二分平均值的方法 AC 。通过设置左、右端点为 0 、1,二分中间值(假设为 \(mid\))并判断是否合法。那么问题就转换为:
给定一个有 0 和 1 组成的序列,求出一个子序列使其数字的平均值大于等于 \(mid\),并输出方案。
但是因为 \(mid\) 值不断变化,不方便求解,所以我们可以开一个临时数组,使里面的值都为 \(a_i-mid\) ,然后再求此序列中是否存在一个子序列使其数字的平均值大于等于 0 ,可以用前缀和 + 贪心解决。
其实没有这一步也可以实现。
至此,幼儿园知识普及完毕问题明显已解决。
注意事项:
- 一定要注意精度问题。
- 输入用 string 更方便。
- 二分中的 \(l\) , \(r\) , \(mid\) 是小数。
- 要开 double !!!
码:
#include<bits/stdc++.h>
using namespace std;
typedef double LD;
const LD eps = 1e-6,inf = 0x3f3f3f3f;
const int N = 100002;
int t,n,m;
string str;
LD a[N],b[N],sum[N];
void input()
{
scanf("%d%d",&n,&m);
cin>>str;
for(int i = 0;i < n;i++) a[i+1] = str[i]-'0';
return;
}
bool check(LD x,bool f)
{
sum[0] = 0;
for(int i = 1;i <= n;i++) b[i] = a[i]-x;
for(int i = 1;i <= n;i++) sum[i] = sum[i-1]+b[i];
LD minn = inf*1.0,ans = -inf*1.0;
int nowl = 1,ansl = 0,ansr = 0;
for (int i = m;i <= n;i++)
{
if (sum[i-m] <= minn) nowl = i-m+1;
minn = min(minn,sum[i-m]);
if(sum[i]-minn > ans+eps) ansl = nowl,ansr = i;
ans = max(ans,sum[i]-minn);
}
if(f) printf("%d %d\n",ansl,ansr);
return ans >= 0;
}
void getans()
{
LD l = 0,r = 1,mid;
while(l < r-eps)
{
mid = (l+r)/2;
if(check(mid,0)) l = mid;
else r = mid;
}
check(l+eps,1);
return;
}
int main()
{
scanf("%d",&t);
while(t--)
{
input();
getans();
}
return 0;
}

浙公网安备 33010602011771号