【贪心】【二分答案】codeforces1119E Pavel and Triangles

探讨如何利用不同长度的棍子组成最多数量的三角形,通过贪心算法实现最优解。

Pavel has several sticks with lengths equal to powers of two.

He has a0 sticks of length 20=1, a1 sticks of length 212^121 2, …, an−1 sticks of length 2n−12^{n−1}2n1.

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 2i2^i2i.

Output
Print a single integer — the maximum possible number of non-degenerate triangles that Pavel can make.

 一个很显然的事实:可能的三角形的形式为 (2i,2j,2j),i≤j(2^i,2^j,2^j) ,i \le j(2i,2j,2j),ij,那么我们可以看出实际上是对a数组不断在某个i下标-1,在某个j下标-2,求最大的操作数量。
 当时犯了一个很愚蠢的错误,认为不断从a[i]a[i]a[i]中取走3到不能取是最优的,然后对3取模再求剩余匹配,产生这种想法很可能是被以往题目误导了,后来想想,a=4,4,4a={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),ijklp可以替换成(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匹配,用pre[i]pre[i]pre[i]记录到i时前面能提供的-1个数,tot[i]tot[i]tot[i]记录到i时需要的-2个数,对任意i,前者都不能比后者小。

#include<cstdio>
#include<algorithm>
#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;
}

 题解提供了一种贪心解法,这也是大部分人的算法。解释如下

可能的三角形的形式为 2i,2j,2j,i&lt;=j2^i,2^j,2^j ,i&lt;=j2i,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<cstdio>
#include<algorithm>
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;
}

 经典题了!一定要记住结论留下印象!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值