一看到这题,以为是数学,真的不会。
事实上这是算法比赛,数学题不一定要用数学的解法。你看这题,要你找某个尽量小的值来满足一些事。简直就是明示你二分啊。而且满足不满足是连续的,因此用lower_bound。
一直觉得一定是有某个公式算出答案。
其实没有那么多公式化的东西,更重要的是解决问题。如果实在没有头绪,那就自己给自己出几个题目,想尽各种办法先得到答案,也许你就会找到规律,从而找到能用代码解决的方法。而不是一开始就去找一个能用代码解决的方法。你至少应该能想到偶数的解法的,而你却没有去做。
首先每个礼物有无穷多个,唯一的区别就是要不同,不妨把礼物编号,然后第一个人取前r[i]个。
偶数的情况会十分简单,因为奇数编号的人只与偶数编号的人相邻,只要让每一对相邻的人都能相安无事便可,我们让奇数号的人从前面取,让偶数号的人从后面取,待取的礼物一定会被释放,有点像滚动数组的感觉。礼物只要大于相邻和的最大值即可。
然而这并没有什么乱用,因为这个方法无法解决奇数的情况,奇数无法像滚动数组那样,更像是动态规划。因此能解决奇数的方法也一定能解决偶数的问题。
直接算的话该如何决策都不知道,只能枚举。也许只有动态规划能解,但这种环状的东西也不知道该怎么办。
决策似乎和礼物数有关,也就是说在知道礼物数之前很难想到某种分配方法。这时就应该要想到用二分了。能想到二分答案,问题就解决了一半。
接下来是如何判断满不满足。
还是一样,礼物编号,第一个人取前r[i]个。那么最后一个人就不能取前r[i]个,但若是他的前一个人抢了某些他的选择,加上礼物数不够,那就会导致分配失败。
因此最后一个人要尽量取后面的,而他的前一个人要尽量取前面的,推到第二个人,那就是偶数要尽量取前面,奇数要尽量取后面。因此就模拟一下,看下能不能就好了。
模拟的方法很重要,优质的模拟方法能大大减少代码量和时间空间复杂度。
最简单无脑的方法莫过于用一个集合或者数组来模拟了吧。。我估计自己也就只能想到这样的方法了。
但是你不必模拟得过于细节,比如具体到礼物的编号,这十分没有必要,我们之关心礼物的个数。
我们只要知道他“前面”取了几个礼物,“后面”取了几个礼物即可。递推时加加减减就出结果了。
前面,后面是相对而言的。因此你要划清一个界限,之前的叫前面,之后的叫后面。这个界限用第一个人取的数r[i]来划分就十分不错。初始化和得出结论都十分方便。
然后就根据前面一个人的取法来得到自己的取法。
至于二分的范围,适当的范围能避免模拟时不必要的讨论,比如中途礼物数不够了,或者说上界明显过大了之类的。
大白书上的范围很值得学习,卡的很稳。不像我直接就全范围二分导致讨论时出错然后WA。
限定下届是为了能稳稳的推到最后,限定上界是为了减少不必要的计算。
若想稳稳的推到最后,可参考偶数的情况。
上界则是3*max(r[i])。本来2*max(r[i])就应该足够,但是为了避免n=3之类的这种情况,又希望能简单点算出来,那就取3*max(r[i])。其实这无关紧要,但我觉得你至少该想想而不是直接上个无脑大的东西。
代码
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
typedef long long ll;
ll n;
ll r[maxn];
ll LEFT[2];
ll RIGHT[2];
ll now;
bool ok(ll MAX)
{
LEFT[now]=r[1];
RIGHT[now]=0;
for(ll i=2;i<=n;i++)
{
if(i&1)
{
RIGHT[now^1]=min(r[i],max(MAX-r[1]-RIGHT[now],0ll));
if(RIGHT[now^1]<r[i]) LEFT[now^1]=r[i]-RIGHT[now^1];
else LEFT[now^1]=0;
}
else
{
LEFT[now^1]=min(r[i],max(r[1]-LEFT[now],0ll));
if(LEFT[now^1]<r[i]) RIGHT[now^1]=r[i]-LEFT[now^1];
else RIGHT[now^1]=0;
}
now^=1;
}
return LEFT[now]==0;
}
int main()
{
while(scanf("%lld",&n)==1&&n)
{
ll x=0;
ll y=0;
now=0;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&r[i]);
y+=r[i];
}
if(n==1) printf("%lld\n",r[1]);
else if(n&1)
{
x=r[1]+r[n];;
for(int i=1;i<n;i++) x=max(x,r[i]+r[i+1]);
y++;
while(x<y)
{
ll m=x+(y-x)/2;
if(ok(m)) y=m;
else x=m+1;
}
printf("%lld\n",x);
}
else
{
ll MAX=r[1]+r[n];
for(ll i=1;i<n;i++)
MAX=max(MAX,r[i]+r[i+1]);
printf("%lld\n",MAX);
}
}
return 0;
}