有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