5.贪心
贪心
开题顺序: \(IHCJEBAD\)
\(A\) [AGC032E] Modulo Pairing
-
若没有 \(\bmod m\) 的限制,将 \(\{ a \}\) 升序排序后取第 \(i\) 大和第 \(i\) 小进行匹配,调整法即可证明。
- 以 \(a \le b \le c \le d\) 为例,由 \(\begin{cases} a+c \le b+c \le b+d \\ a+d \le b+d \end{cases}\) ,可知 \(\max(a+d,b+c) \le \max(a+c,b+d)\) 。
-
类似 [ABC353C] Sigma Problem ,加上 \(\bmod m\) 的限制后一定会被一个分界点分成左右两部分,左右两部分分别取第 \(i\) 大和第 \(i\) 小进行匹配。
- 证明仍考虑调整法,假设 \(a \le b \le c \le d\) 。
- \(a,b,c,d\) 任意两数相加的和 \(<m,\ge m\) 的情况同上。
- 不妨假设 \(a+b<m,c+d \ge m\) 。
- 若 \(a+c \ge m,b+d \ge m\) ,显然有 \(\begin{cases} \max(a+b,c+d-m) \le \max(a+c-m,b+d-m) \\ \max(a+b,c+d-m) \le \max(b+c-m,a+d-m) \end{cases}\) 。
- 若 \(a+d<m,b+c \ge m\) ,显然有 \(\begin{cases} \max(a+b,c+d-m) \le \max(a+c,b+d-m) \\ \max(a+b,c+d-m) \le \max(b+c-m,a+d) \end{cases}\) 。
- 证明仍考虑调整法,假设 \(a \le b \le c \le 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(sum-2k,cnt)\) 个馅饼,而剩下 \(cnt-\min(sum-2k,cnt)\) 个物品需要全价购买。
-
尝试开始反悔,设当前取出的小根堆堆顶为 \(val\) 并弹出,由反悔贪心决策的替换性我们无法直接得知 \(c,val\) 的大小关系。
- 设 \(val\) 的来源是 \(x\) ,每两个馅饼一起考虑。
- 若 \(c>val\) ,我们可以用原来用来白嫖 \(val\) 的机会替换成白嫖 \(c\) ,同时购买 \(val\) ,又因为此时 \(val\) 的来源是一个比 \(c\) 大的馅饼,故一共可以获得两次白嫖机会,将 \(2\) 个 \(c\) 加入 \(Q\) 。
- 若 \(c \le val\) ,存在两种决策:购买 \(x,val\) ,白嫖 \(2\) 个 \(c\) ,代价为 \(x+val\) ;购买 \(x\) 和 \(2\) 个 \(c\) ,仍白嫖 \(val\) ,代价为 \(x+2c\) 。这两个决策是互斥的,故将 \(2c-val>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] 建筑抢修
-
按照 \(t_{2}\) 排序后在保证抢修建筑最多的情况下花费时间最少,使用优先队列存储先前最大的 \(t_{1}\) 支持撤销。
点击查看代码
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
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18650825,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。

森林深处,阿瑞斯静立于巍峨的树冠之上,默默守护着这片生机勃勃的土地。雄鹰展翅,划破长空,带着阿瑞斯的意志俯瞰着森林与大地,迎接风的洗礼。
浙公网安备 33010602011771号