E - Average and Median(平均数,中位数转化,二分,dp)

10 篇文章 0 订阅
10 篇文章 4 订阅

Linking


题意:

n n n 个数中挑选若干数。条件:对于每个位置 i i i,该位置和下一位置至少有一个被选。
问,挑选的数的 平均数中位数 最大分别为多少? (中位数:除2上取整)

思路:

发现如果一个数列满足其平均数不小于 mid,那么 mid 就可以往右走,继续判断,直到不满足了,那么这个 mid 就是能够满足的最大平均数。于是就可以用 二分答案。中位数同理。

碰见平均数和中位数就想转化
  1. 平均数:
    给出一个平均数 mid,判断是否能够挑选出若干数其平均数不低于 mid 呢?如果是,就往右走。
    判断平均数的经典策略:将所有数全部减去平均数 mid,判断是否有若干数之和不小于 0。
    如果是,那么这些数的平均值就不小于 mid。

    那么就要挑出若干个满足限制的数,使得总和最大。判断这个总和是否不小于 mid。

  2. 中位数:
    给出一个中位数 mid,判断是否能够挑选出若干数其中位数不低于 mid 呢?如果是,就往右走。
    判断中位数:如果一个数大于等于 mid,就标记为 1;否则标记为 -1。判断是否有若干数标记之和大于 0。
    如果是,那么这些数的中位数就不小于 mid。

    那么就是要挑选若干个满足限制的数,使得标记总和最大。判断这个总和是否大于 0。

那么接下来就看如何挑选出满足条件的数,使其总和最大?

每个位置 i,该位置和下一位置至少有一个被挑选。

  • 当前位置不选,那上一位置就必须选,从上一位置转移。
  • 当前位置选,那上一位置可选可不选,从上一位置或者上上位置转移。

定义 dp[i][0]:第 i 个位置不选,前i个位置中满足条件的最大和。
dp[i][1]:第 i 个位置选,前i个位置中满足条件的最大和。

那么:dp[i, 0] = dp[i-1, 1]; dp[i, 1] = max(dp[i-1, 1], dp[i-1, 0]) + b[i];
最终整个数列的最大和便为,最后一个位置选或者不选的最大值:max(dp[n, 0] , dp[n, 1]);

还有一种思路是:
定义 dp[i]:第 i 个位置选,前 i 个位置中满足条件的最大和。
那么 dp[i] = max(dp[i-1], dp[i-2]) + b[i];
那么最后一个位置不选时,前 n 个位置最大和为 dp[n-1],最大和:max(dp[n-1], dp[n]);

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, k; 
double a[N], b[N], dp[N][2];
int f[N], ans[N];

bool check(double mid)
{
	for(int i=1;i<=n;i++) b[i]=a[i]-mid;
	
	for(int i=1;i<=n;i++)
		dp[i][0] = dp[i-1][1], dp[i][1] = max(dp[i-1][1], dp[i-1][0])+b[i];
//		dp[i] = max(dp[i-1], dp[i-2]) + b[i];
	
	return max(dp[n][0], dp[n][1]) >= 0;
	//	return max(dp[n-1], dp[n]) >= 0;
}

bool checkk(int mid)
{
	for(int i=1;i<=n;i++) b[i] = (a[i]>=mid)?1:-1;
	
	for(int i=1;i<=n;i++)
		dp[i][0] = dp[i-1][1], dp[i][1] = max(dp[i-1][1], dp[i-1][0])+b[i];
//		dp[i] = max(dp[i-1], dp[i-2]) + b[i];
	
	return max(dp[n][0], dp[n][1]) > 0;
	//	return max(dp[n-1], dp[n]) > 0;
}

signed main(){
	cin>>n;
	
	double maxa=0;
	for(int i=1;i<=n;i++) cin>>a[i], maxa=max(maxa, a[i]);
	
	double l=0, r=maxa; //二分平均数
	while(r-l>1e-6)
	{
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.10f\n", r);
	
	int ll=1, rr=1e9; //二分中位数
	while(ll<rr)
	{
		int mid=ll+rr+1>>1;
		if(checkk(mid)) ll=mid;
		else rr=mid-1;
	}
	cout<<rr;
	
	return 0;
}

经验:

1.平均数,中位数的二分判断;
2. 平均数,中位数判断的转化;(很常用!)
3. 对于 dp 的理解。(场上没想到用dp。。)

很好的一道题!

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值