5.贪心

贪心

开题顺序: IHCJEBAD

A [AGC032E] Modulo Pairing

  • 若没有 modm 的限制,将 {a} 升序排序后取第 i 大和第 i 小进行匹配,调整法即可证明。

    • abcd 为例,由 {a+cb+cb+da+db+d ,可知 max(a+d,b+c)max(a+c,b+d)
  • 类似 [ABC353C] Sigma Problem ,加上 modm 的限制后一定会被一个分界点分成左右两部分,左右两部分分别取第 i 大和第 i 小进行匹配。

    • 证明仍考虑调整法,假设 abcd
      • a,b,c,d 任意两数相加的和 <m,m 的情况同上。
      • 不妨假设 a+b<m,c+dm
      • a+cm,b+dm ,显然有 {max(a+b,c+dm)max(a+cm,b+dm)max(a+b,c+dm)max(b+cm,a+dm)
      • a+d<m,b+cm ,显然有 {max(a+b,c+dm)max(a+c,b+dm)max(a+b,c+dm)max(b+cm,a+d)
  • 在满足右边条件的限制下分界点越靠左越好,二分判断即可。

    点击查看代码
    ll a[200010];
    bool check(ll mid,ll n,ll m)
    {
    	for(ll i=mid+1,j=n;i<=j;i++,j--)
    	{
    		if(a[i]+a[j]<m)
    		{
    			return false;
    		}
    	}
    	return true;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,ans=0,l,r,mid,pos=0,i,j;
    	cin>>n>>m;
    	n*=2;
    	l=0;
    	r=n/2;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid*2,n,m)==true)
    		{
    			pos=mid*2;
    			r=mid-1;
    		}
    		else
    		{
    			l=mid+1;
    		}
    	}
    	for(i=1,j=pos;i<=j;i++,j--)
    	{
    		ans=max(ans,a[i]+a[j]);
    	}
    	for(i=pos+1,j=n;i<=j;i++,j--)
    	{
    		ans=max(ans,a[i]+a[j]-m);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

B luogu P3265 [JLOI2015] 装备购买

C luogu P1484 种树

  • 学习笔记

  • 反悔贪心板子。

    点击查看代码
    priority_queue<pair<ll,ll> >q;
    ll a[300010],pre[300010],suf[300010],vis[300010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,k,ans=0,pos,i;
    	cin>>n>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		pre[i]=i-1;
    		suf[i]=i+1;
    		q.push(make_pair(a[i],i));
    	}
    	a[0]=a[n+1]=-0x3f3f3f3f;
    	for(i=1;i<=k;i++)
    	{
    		while(q.empty()==0&&vis[q.top().second]==1)
    		{
    			q.pop();
    		}
    		if(q.empty()==0)
    		{
    			if(q.top().first<=0)
    			{
    				break;
    			}
    			ans+=q.top().first;
    			pos=q.top().second;
    			q.pop();
    			vis[pre[pos]]=vis[suf[pos]]=1;
    			a[pos]=a[pre[pos]]+a[suf[pos]]-a[pos];
    			q.push(make_pair(a[pos],pos));
    			pre[pos]=pre[pre[pos]];
    			suf[pos]=suf[suf[pos]];
    			pre[suf[pos]]=suf[pre[pos]]=pos;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

D CF335F Buy One, Get One Free

  • 考虑最大化白嫖的馅饼价格总和。

  • 通过样例 1 可知,不断拿最大白嫖次大的贪心策略是假的,考虑反悔贪心维护白嫖物品的集合 Q

  • 不妨把同样价格的馅饼缩在一起处理。并按照价格从大到小进行处理,此时白嫖只能依赖先前的状态。

  • 设当前价格为 c ,数量为 cnt ,之前一共得到了 sum 个馅饼且有 k=|Q| 个馅饼是通过白嫖得到的。

  • 此时可以白嫖 min(sum2k,cnt) 个馅饼,而剩下 cntmin(sum2k,cnt) 个物品需要全价购买。

  • 尝试开始反悔,设当前取出的小根堆堆顶为 val 并弹出,由反悔贪心决策的替换性我们无法直接得知 c,val 的大小关系。

    • val 的来源是 x ,每两个馅饼一起考虑。
    • c>val ,我们可以用原来用来白嫖 val 的机会替换成白嫖 c ,同时购买 val ,又因为此时 val 的来源是一个比 c 大的馅饼,故一共可以获得两次白嫖机会,将 2c 加入 Q
    • cval ,存在两种决策:购买 x,val ,白嫖 2c ,代价为 x+val ;购买 x2c ,仍白嫖 val ,代价为 x+2c 。这两个决策是互斥的,故将 2cval>0,val 加入 Q
  • 为避免自己白嫖自己的情况,中间需要用数组转存一下加入集合 Q 的价格。

    点击查看代码
    ll a[500010],b[500010],cnt[500010];
    vector<ll>tmp;
    priority_queue<ll,vector<ll>,greater<ll> >q;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,ans=0,sum=0,tot,val,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		b[i]=a[i];
    		ans+=a[i];
    	}
    	sort(b+1,b+1+n);
    	b[0]=unique(b+1,b+1+n)-(b+1);
    	for(i=1;i<=n;i++)
    	{
    		cnt[lower_bound(b+1,b+1+b[0],a[i])-b]++;
    	}
    	for(i=b[0];i>=1;i--)
    	{
    		tot=min(sum-2*(ll)q.size(),cnt[i]);
    		for(j=1;j<=tot;j++)
    		{
    			tmp.push_back(b[i]);
    		}
    		tot=cnt[i]-tot;
    		for(j=1;j<=tot&&q.empty()==0;j+=2)
    		{
    			val=q.top();
    			q.pop();
    			if(b[i]>val)
    			{
    				tmp.push_back(b[i]);
    				if(j+1<=tot)
    				{
    					tmp.push_back(b[i]);
    				}
    			}
    			else
    			{	
    				tmp.push_back(val);
    				if(j+1<=tot&&2*b[i]-val>0)
    				{
    					tmp.push_back(2*b[i]-val);
    				}
    			}
    		}
    		while(tmp.empty()==0)
    		{
    			q.push(tmp.back());
    			tmp.pop_back();
    		}
    		sum+=cnt[i];
    	}	
    	while(q.empty()==0)
    	{
    		ans-=q.top();
    		q.pop();
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

E [AGC010C] Cleaning

F luogu P9293 [ROI 2018] Addition without carry

G LibreOJ 560. 「LibreOJ Round #9」Menci 的序列

H luogu P4053 [JSOI2007] 建筑抢修

  • 按照 t2 排序后在保证抢修建筑最多的情况下花费时间最少,使用优先队列存储先前最大的 t1 支持撤销。

    点击查看代码
    pair<ll,ll>a[150010];
    priority_queue<ll>q;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,ans=0,sum=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].second>>a[i].first;
    	}
    	sort(a+1,a+1+n);
    	for(i=1;i<=n;i++)
    	{
    		if(sum+a[i].second<=a[i].first)
    		{
    			sum+=a[i].second;
    			ans++;
    			q.push(a[i].second);
    		}
    		else
    		{
    			if(q.empty()==0&&q.top()>=a[i].second)
    			{
    				sum+=a[i].second-q.top();
    				q.pop();
    				q.push(a[i].second);
    			}
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    

I luogu P3620 [APIO/CTSC2007] 数据备份

  • 多倍经验: SP1553 BACKUP - Backup Files | CF958E2 Guard Duty (medium)

  • 每个办公楼显然只能和相邻的两个办公楼连接,且不能同时连接。

  • 然后和 luogu P1484 种树 一样做即可。

    点击查看代码
    ll a[100010],b[100010],pre[100010],suf[100010],vis[100010];
    priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,k,ans=0,pos,i;
    	cin>>n>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		b[i]=a[i+1]-a[i];
    		pre[i]=i-1;
    		suf[i]=i+1;
    		q.push(make_pair(b[i],i));
    	}
    	b[0]=b[n]=0x7f7f7f7f;
    	for(i=1;i<=k;i++)
    	{
    		while(q.empty()==0&&vis[q.top().second]==1)
    		{
    			q.pop();
    		}
    		if(q.empty()==0)
    		{
    			ans+=q.top().first;
    			pos=q.top().second;
    			q.pop();
    			vis[pre[pos]]=vis[suf[pos]]=1;
    			b[pos]=b[pre[pos]]+b[suf[pos]]-b[pos];
    			q.push(make_pair(b[pos],pos));
    			pre[pos]=pre[pre[pos]];
    			suf[pos]=suf[suf[pos]];
    			suf[pre[pos]]=pre[suf[pos]]=pos;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

J [AGC008B] Contiguous Repainting

posted @   hzoi_Shadow  阅读(28)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
扩大
缩小
点击右上角即可分享
微信分享提示