1. 程式人生 > >#10012. 「一本通 1.2 例 2」Best Cow Fences

#10012. 「一本通 1.2 例 2」Best Cow Fences

【題目描述】

原題來自: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;
}

最後說一下,這道題其實也可以用斜率優化來做,可惜我不會