#10012. 「一本通 1.2 例 2」Best Cow Fences
阿新 • • 發佈:2018-12-22
【題目描述】
原題來自:USACO 2003 Mar. Green
給定一個長度為 n 的非負整數序列 A,求一個平均數最大的,長度不小於 L 的子段。
【輸入格式】
第一行用空格分隔的兩個整數 n 和 L;
第二行為 n 個用空格隔開的非負整數,表示 Ai。
【輸出格式】
輸出一個整數,表示答案的 1000 倍。不用四捨五入,直接輸出。
【樣例輸入】
10 6
6 4 2 10 3 8 5 9 4 1
【樣例輸出】
6500
【資料範圍與提示】
n≤10^5, 0≤Ai≤2000。
思路:用二分列舉平均值mid,每個牛的價值都減去mid,
看是否有連續的超過L長度的區間使得這段區間的價值大於等於0,
如果能找到,那麼說明這個平均值可以達到。不得不承認,總覺得二分的題不記錄一下區間值是完成不了二分的,這道題也是這樣,判斷區間值是否大於0,如果大於0的話,當前這個區間成立。
/* 思路:用二分列舉平均值mid,每個牛的價值都減去mid, 看是否有連續的超過L長度的區間使得這段區間的價值大於等於0, 如果能找到,那麼說明這個平均值可以達到。 */ #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; inline int read()//快讀 { char c=getchar(); int x=0,f=1; while(c<48 || c>57) { if(c=='-') f=-1; c=getchar(); } while(c>=48 && c<=57) { x=x*10+c-48; c=getchar(); } return x*f; } ll a[110000],s[110000]; ll n,L; ll ans; bool check(ll x) { for(int i=1;i<=n;i++) { s[i]=s[i-1]+a[i]-x;//以i為結尾的區間和 /* a[i]-x等於每頭牛的價值減去中間值 得出當前這個區間的價值 加上前面的 就是區間和 */ } ll minn=99999999; for(int i=L;i<=n;i++)//列舉結束位置 { minn=min(minn,s[i-L]);//最小的區間和 if(s[i]-minn>=0) return true; /* 減去最小的區間和有大於0的話就說明有價值 有平均值 */ } return false; } int main() { n=read(); L=read(); ll l,r; for(int i=1;i<=n;i++) { a[i]=read(); a[i]*=1000;//先乘不用後面乘,防小數點 l=min(l,a[i]); r=max(r,a[i]); //a[i]中的最小極限和最大極限,沒有必要定義0-9999999 //當然也可以定義到0-9999999,怕忘記的可以直接先定義 } while(l<=r)//二分 { int mid=(l+r)/2; if(check(mid)==true) { ans=mid; l=mid+1; } else r=mid-1; } printf("%lld\n",ans); return 0; }
順便帶一下那個二分的l和r
把二分的意義搞清楚之後以後的題目判斷l和r就不用擔心了
【另一種打法】
思路:
題意:給你n個牛的自身價值,讓你找出連續的且數量大於等於F的一段區間,使這段區間內的牛的平均價值最大。
思路:用二分列舉平均值ave,每個牛的價值都減去ave,看是否有連續的超過f長度的區間使得這段區間的價值大於等於0,如果能找到,那麼說明這個平均值可以達到。先每個a[i]減去mid得到b[i],用dp[i]表示以i為結尾區間連續長度大於等於f的最大連續區間和,maxx[i]表示以i為結尾的最大連續區間和,sum[i]表示1~i的價值總和那麼maxx[i]=max(maxx[i-1]+b[i],b[i]),dp[i]=maxx[i-f+1]+sum[i]-sum[i-f+1],判斷是否有一個i(i>=f)滿足dp[i]>=0.
#include<iostream>
#include<stdio.h>
#include<string>
#include<algorithm>
using namespace std;
#define inf 99999999
#define maxn 100050
#define eps 1e-6
double sum[maxn],a[maxn],b[maxn],dp[maxn],maxx[maxn];
int main()
{
int n,m,i,j,f,ant;
double l,r,mid,ans;
scanf("%d%d",&n,&f);
l=2000.0;
r=1.0;
for(i=1; i<=n; i++)
{
scanf("%lf",&a[i]);
l=min(l,a[i]);
r=max(r,a[i]);//這裡不能省,不然會影響精度
}
while(r-l>eps)
{
mid=(l+r)/2.0;
sum[0]=0;
maxx[0]=0;
for(i=1; i<=n; i++)
{
b[i]=a[i]-mid;
sum[i]=sum[i-1]+b[i];
maxx[i]=max(b[i],maxx[i-1]+b[i]);
}
ans=sum[f];
for(i=f+1; i<=n; i++)
{
dp[i]=maxx[i-f+1]+sum[i]-sum[i-f+1];
if(ans<dp[i])ans=dp[i];
}
if(ans>=0)l=mid;
else r=mid;
}
ant=1000*r;
printf("%d\n",ant);
return 0;
}
最後說一下,這道題其實也可以用斜率優化來做,可惜我不會