贪心题目总结,hdu 6301 Distinct Values
一些经典贪心题目.
1. 给定$m$个区间, 求一个字典序最小的序列, 满足$m$个区间内数均不相同.
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。2. 给定$m$个有序对$(a,b)$, 求构造一个$n$排列, 满足$m$个对中$a$均排在$b$前, 且$1$尽量靠前, 在$1$尽量靠前的前提下$2$尽量靠前,....以此类推
1, 调整法.
先构造出一个可行解, 然后在不破坏解的可行性条件下, 不断调整到最优.
练习1. cf 913C
大意: n种汽水, 每种无限多, 第$i$种体积$2^{i-1}$, 花费$a_i$, 求总体积不少于$l$时的最少花费.
从第1种开始向后调整, 假设遍历到第$i$种, 若$i$的单价最低, 显然要把$i$之前选的调整为若干个$2^{i-1}$. 要注意可能存在之前的凑不够$2^{i-1}$的部分的总价少于$a[i]$, 也要调整.
#include <iostream> #include <cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) #define PER(i,a,n) for(int i=n;i>=a;--i) using namespace std; typedef long long ll; const int N = 1e6+10; int n, l, a[N], c[N]; int main() { scanf("%d%d", &n, &l); REP(i,0,n-1) scanf("%d", a+i); int opt = 0; c[0] = l; REP(i,1,n-1) { if (a[i]<=((ll)a[opt]<<i-opt)) { int t = c[opt]>>i-opt; c[opt] -= t<<i-opt; c[i] += t; opt = i; } int t = 0; ll w = 0; PER(j,0,i-1) if (c[j]) { int cnt = min(c[j], (((1<<i)-t+(1<<j)-1)>>j)-1); t += cnt<<j, w += (ll)a[j]*cnt; } if (w>a[i]) { t = 0, ++c[i]; PER(j,0,i-1) if (c[j]) { int cnt = min(c[j], (((1<<i)-t+(1<<j)-1)>>j)-1); c[j] -= cnt; } } } ll ans = 0; REP(i,0,n-1) ans+=(ll)a[i]*c[i]; printf("%lld\n", ans); }
练习2. cf 916B
大意: 求将n划分为k个2的幂的和, 且最大幂最小,字典序尽量大.
(1)先考虑将n划分为2的幂的和, 显然直接按二进制划分即可, 假设划分出$x$个数.
(2)考虑如何调整为k个, 注意到一个高次幂可以分为为两个更低次幂, 相当于对个数贡献加一, 所以只要$k\ge x$, 那么一定可以划分成功.
(3)要使最大幂最小, 我们可以在(2)中调整时先拆高次幂即可, 因为显然拆低次幂不会更优.
(4)考虑字典序最大, 也就是要保证个数和最大幂不变的前提下使字典序增加. 一个显然的方案是先选两个尽可能大的低次幂合并为高次幂, 为了使个数不变, 再选一个高次幂转化为两个低次幂.
总调整次数是$O(k)$的, 用stl的map实现, 复杂度就为$O(klogk)$
#include <iostream>
#include <cstdio> #include <map> #include <set> using namespace std; long long n; int k; int main() { scanf("%lld%d", &n, &k); int cnt = 0; map<int,int,greater<int> > s; for (int i=0; i<=62; ++i) if (n>>i&1) ++s[i],++cnt; if (k<cnt) return puts("No"),0; while (cnt<k) { auto t = s.begin(); int tt = t->first; if (!--s[tt]) s.erase(t); s[tt-1] += 2; ++cnt; } set<int,greater<int> > r; int mx = s.begin()->first; for (auto t:s) if (t.first!=mx&&t.second>=2) { r.insert(t.first); } while (r.size()) { int x = *r.begin(); ++s[x+1], s[x]-=2; if (s[x]<2) r.erase(x); if (x+1!=mx&&s[x+1]>=2) r.insert(x+1); if (!s[x]) s.erase(x); auto t = --s.end(); int tt = t->first; if (tt==x+1) { --s[x+1], s[x]+=2; break; } if (--s[tt]<2&&r.count(tt)) r.erase(tt); if (!s[tt]) s.erase(tt); s[tt-1] += 2, r.insert(tt-1); } puts("Yes"); for (auto t:s) { while (t.second--) printf("%d ", t.first); } puts(""); }

更多精彩