Pavel has several sticks with lengths equal to powers of two.
He has a0 sticks of length 20=1, a1 sticks of length 2 1 2^1 21 2, …, an−1 sticks of length 2 n − 1 2^{n−1} 2n−1.
Pavel wants to make the maximum possible number of triangles using these sticks. The triangles should have strictly positive area, each stick can be used in at most one triangle.
It is forbidden to break sticks, and each triangle should consist of exactly three sticks.
Find the maximum possible number of triangles.
Input
The first line contains a single integer n (1≤n≤300000) — the number of different lengths of sticks.
The second line contains n integers a0, a1, …, an−1 (1≤ai≤109), where ai is the number of sticks with the length equal to 2 i 2^i 2i.
Output
Print a single integer — the maximum possible number of non-degenerate triangles that Pavel can make.
一个很显然的事实:可能的三角形的形式为 ( 2 i , 2 j , 2 j ) , i ≤ j (2^i,2^j,2^j) ,i \le j (2i,2j,2j),i≤j,那么我们可以看出实际上是对a数组不断在某个i下标-1,在某个j下标-2,求最大的操作数量。
当时犯了一个很愚蠢的错误,认为不断从 a [ i ] a[i] a[i]中取走3到不能取是最优的,然后对3取模再求剩余匹配,产生这种想法很可能是被以往题目误导了,后来想想, a = 4 , 4 , 4 a={4,4,4} a=4,4,4就轻松当反例了。
但当时有一个想法是没问题的,那就是如果存在一组最优解,可以尽量把-2操作的j下标替换成尽量大的j’,因为如果存在 ( i , j , j ) , ( k , l , l ) , ( k , p , p ) , i ≤ j ≤ k ≤ l ≤ p (i,j,j),(k,l,l),(k,p,p), i\le j\le k\le l\le p (i,j,j),(k,l,l),(k,p,p),i≤j≤k≤l≤p可以替换成 ( i , k , k ) , ( j , l , l ) , ( j , p , p ) (i,k,k),(j,l,l),(j,p,p) (i,k,k),(j,l,l),(j,p,p) (这里是k被使用的情况,未被使用当然更可以了)
于是考虑二分答案,答案就是-2操作的组数,check时,把-2尽量靠后排列,然后检查一下能不能把这些-2和之前的-1匹配,用 p r e [ i ] pre[i] pre[i]记录到i时前面能提供的-1个数, t o t [ i ] tot[i] tot[i]记录到i时需要的-2个数,对任意i,前者都不能比后者小。
#include
#include
#define M (L+R+1>>1)
using namespace std;
using LL=long long;
int n,a[300005],b[300005],c[300005];
LL L,R,pre[300005];
bool check(LL x)
{
for(int i=1;i<=n;i++)
b[i]=a[i],c[i]=0;
LL tot=0;
for(int i=n;i>0&&tot<x;i--)
if(b[i]/2+tot<=x)
c[i]=b[i]/2,tot+=b[i]/2,b[i]%=2;
else
c[i]=x-tot,b[i]-=(x-tot)*2,tot=x;
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+b[i];
if(tot<x)
return false;
tot=0;
for(int i=1;i<=n;i++)
{
tot+=c[i];
if(tot>pre[i])
return false;
}
return true;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
L=0;
R=n*1E9;
while(L<R)
if(check(M))
L=M;
else
R=M-1;
printf("%lld",L);
return 0;
}
题解提供了一种贪心解法,这也是大部分人的算法。解释如下
可能的三角形的形式为 2 i , 2 j , 2 j , i < = j 2^i,2^j,2^j ,i<=j 2i,2j,2j,i<=j
贪心算法:从小到大考虑j,对未匹配的i尽量寻找的两个j匹配,再尽量创造用三个j的三角形,剩余的j加入未匹配量中,再考虑下一个j
证明:对于某个最优解,寻找第一个我们的解法中与其不同的三角形
假设这个三角形为(i,j,j)形式:
如果i,j,j三者被用在了新最优解的三个三角形中,(i,k1,k1),(j,k2,k2),(j,k3,k3),那么可以修改成(i,j,j),(k1,k2,k2),(k1,k3,k3)
如果其中两项被用在了两个三角形中,剩下一个被抛弃不用,(i,k1,k1)(j,k2,k2)或者(j,k1,k1)(j,k2,k2),显然用上被抛弃的那个组成(i,j,j)不会更差
如果两项被用在同一个三角形中,剩下一个被抛弃,只可能有(j,j,j)当然也可以被(i,j,j)替代
如果一项被用,剩下两个被抛弃,当然也可以被替代。
没有一项被用是不可能的。
假设这个三角形是(j,j,j)形式,因为之前贪心的过程两者相同,所以不可能两个j在一个三角形中,只可能是(j,k1,k1) , (j,k2,k2), (j,k3,k3)形式,也可以重组替代。
综上任意一个最优解可以被等价替换为我们贪心解。所以这么解最优。
#include
#include
using namespace std;
using LL=long long;
int n,a,rem,t;
LL ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
t=min(a/2,rem);
rem-=t;
ans+=t;
a-=t*2;
rem+=a%3;
ans+=a/3;
}
printf("%lld",ans);
return 0;
}
经典题了!一定要记住结论留下印象!