admin 管理员组

文章数量: 887042

P

有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]。

求最小值最大二分问题

我们可以对最小值进行枚举然后进行二分进而求出最大值

两个数组a[N],b[N];

当我们从后边转移过来的b[i]>=mid的时候我们可以将a[i]的全部转移到前面,注意题目是从前往后所以我们向前转移的时候是不会超过a[i]本身的

那么当b[i]<mid的时候我们将可以对a[i]和b[i]+a[i]-mid取一个最小值然后用这个最小值向前面转移

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];int b[N];
int maxx;int n;
bool check(int mid)
{for(int i=1;i<=n;i++) b[i]=0; for(int i=n;i>=3;i--){if(a[i]+b[i]>=mid){if(b[i]>=mid){b[i-1]+=(a[i]/3);b[i-2]+=2*(a[i]/3);}else{int x=min(a[i]+b[i]-mid,a[i]);b[i-1]+=(x/3);b[i-2]+=2*(x/3);}			}else{return 0;}}if(a[1]+b[1]<mid || a[2]+b[2]<mid) return 0;return 1;}
void solve()
{maxx=-1;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];maxx=max(maxx,a[i]);}int l=0,r=maxx;while(l<=r){int mid=(l+r)/2;if(check(mid)){l=mid+1;}elser=mid-1;}cout<<l-1<<endl;
}
int main()
{int t;cin>>t;while(t--){solve();}
}

本文标签: P