C. Balanced Stone Heaps(二分 + 逆向思维)

这篇博客详细解析了Codeforces上的Problem-1623C,这是一个关于移动石头的游戏问题。博主通过从后往前处理石堆的方法,提出了一种二分查找的解决方案,旨在找到最小堆的最大石子数。文章通过实例解释了如何进行石头的转移,并展示了如何判断和优化石子分配以达到目标。最后,博主给出了完整的C++代码实现,帮助读者理解和解决此类问题。
摘要由CSDN通过智能技术生成

Problem - 1623C - Codeforces

 

有n堆石头。第i个堆有hi个石头。你想通过执行以下过程来改变堆中石头的数量。

从第3个堆到第n个堆,你按照这个顺序走一遍。
设i为当前堆的编号。
你可以选择一个数字d(0≤3⋅d≤hi),从第i堆移出d个石头到第(i-1)堆,再从第i堆移出2⋅d个石头到第(i-2)堆。
因此,之后hi减少了3⋅d,hi-1增加了d,hi-2增加了2⋅d。
你可以为不同的操作选择不同或相同的d。有些堆可能变成空的,但它们仍然算作堆。
在这个过程中,最小的堆中最大的棋子数是多少?

输入
每个测试包含多个测试用例。第一行包含测试用例的数量t(1≤t≤2⋅105)。测试用例的描述如下。

每个测试用例的第一行包含一个整数n(3≤n≤2⋅105)。

每个测试用例的第二行包含n个整数h1,h2,h3,...,hn(1≤hi≤109)。

保证所有测试用例的n之和不超过2⋅105。

输出
对于每个测试案例,打印出最小的堆所能包含的最大石子数。

例子
输入复制
4
4
1 2 10 100
4
100 100 100 1
5
5 1 1 1 8
6
1 2 3 4 5 6
输出拷贝
7
1
1
3
注意
在第一个测试案例中,初始堆大小为[1,2,10,100]。我们可以按以下方式移动棋子。

将3个石头和6个石头分别从第3堆移到第2堆和第1堆。堆的大小将是[7,5,1,100]。
从最后一个堆中的6个石头和12个石头分别移到第3和第2个堆中。堆的大小将是[7,17,7,82]。
在第二个测试案例中,最后一个堆是1,我们不能增加其大小。

在第三个测试案例中,最好不要移动任何石头。

在最后一个测试案例中,最终可实现的堆的配置可以是[3,5,3,4,3,3]。

题解:
看到让我们求最小堆的最大值就可以明白,让我们二分最小堆的最大值

关键是如何二分

如果按题目说让我们从前往后处理石堆,

我们会发现此时i堆的石子情况有i+1与i+2堆石子决定,很难去考虑如何分配

那我们就从后往前处理

如果当前满足,尽量往前拿多点石子,以让前面满足

但是要注意往前拿的石子总数不可超过当前堆的实际石子总数(本题最重要的部分,好好理解)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define int long long
int a[200050];
int b[200050];
int n;
int check(int x)
{
	for(int i = 0;i <= n - 1;i++)
	b[i] = a[i+1];
	for(int i = n-1;i >= 2;i--)
	{
		if(b[i] < x)
		return 0;
		int y = min(a[i+1],b[i] - x)/3;
		b[i-1] += y;
		b[i-2] += y*2;
	}
	if(b[0]<x||b[1]<x)
	return 0;
	return 1;
	
}
void solve()
{
	cin >> n;
	int r = 0;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		r = max(r,a[i]);
	}
	int l = 0;
	while(l <= r)
	{
		int mid = (l + r)/2;
		if(check(mid))
		{
			l = mid + 1;
		}
		else
		{
			r =  mid - 1;
		}
	}
	cout<< l - 1<<"\n";
}
signed main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	}
}

//1 10 11

//001
//010
//011
//100

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值