本文共 8551 字,大约阅读时间需要 28 分钟。
贪心就是套方案或者数学证明呗,题做多了就有感觉了。
贪心法的证明大致有3种:贪心中真的很多排序,堆,pair,结构体,要想清楚他们维护的是什么,考验的是基础的代码实现逻辑和双指针算法等。还有很多边界问题,比如前导0,相等,奇偶数…
书上的题:
区间调度问题: 这个我们都知道,选结束时间最早的嘛。 因为是对结束区间排序,我们把结束端点放进pair的first里,开始端点放在pair的second里。pairp[N];void solve(){ sort(p,p+n); int t=p[0].first; for(int i=1;i t) { p[i].first=t; ans++; } }}
字典序最小问题
这题重要在对于一些特殊值,比如边界,相等的情况的处理。 这题的贪心策略是将S与S翻转过的字符串比较(不是真翻转,就是用指针倒着数即可),因为当头字符和尾字符相等,我们必须比较去掉他们后的下一个字符再做定论。void solve(){ int i=0,j=n-1; while(i<=j) { bool flag=0; for(int k=0;ks[b-k]) { flag=0; break; } if(a[a+k]
Saruman’s Army
这个策略也很简单,我们先找到能覆盖当前点的最小点,再找到这个点能覆盖到的最右边的点。如此循环。void solve(){ sort(a,a+n); int i=0,ans=0; while(i
Fence Repair
这是一道经典的合并果子。。。 (合并果子等会会写到)谈谈贪心的常用方式
贪心会与二分值的验证结合洛谷的贪心题单(只做了提高组)
P2240部分背包问题 这根本不是个背包好吧,当然是先放单位重量价格最高的啦0v- 另外注意浮点数,不要直接比大小。#includeusing namespace std;const int N=1010;double w[N],v[N]; pair a[N];int main(){ int n; double t; cin>>n>>t; for(int i=0;i >w[i]>>v[i]; a[i].first=w[i]/v[i]; a[i].second=i; } double sum=0,res=0; sort(a,a+n); for(int i=0;i
P1090合并果子
#includeusing namespace std;const int N=10100;int a[N];priority_queue q;int main(){ int n; cin>>n; for(int i=0;i >a[i]; q.push(-a[i]); } int res=0,x1,x2; while(q.size()!=1) { x1=q.top(); q.pop(); x2=q.top(); q.pop(); res=res-x1-x2; q.push(x1+x2); } cout<
P1106删数问题
这道题想出贪心策略后还要考虑两点:#includeusing namespace std;int a[500];int main(){ string m; cin>>m; int k; cin>>k; int t=1; int flag=0,cnt=0; for(int i=1;i<=m.length();i++) { a[i]=m[i-1]-'0'; } int rest=m.length()-k; while(cnt
P4995跳跳!
我是把他想成了一根直线,从一端跳到另一端可以用双指针。#includeusing namespace std;const int N=310;long long h[310];int main(){ int n; cin>>n; for(int i=0;i >h[i]; } sort(h,h+n); long long res=0; if(h[n-1]>(-h[0])||h[0]>=0) { res+=h[n-1]*h[n-1]; reverse(h,h+n); } else { res+=h[0]*h[0]; } int i=0,j=n-1; if(n%2==0) { while(i-j!=1) { res=res+(h[i]-h[j])*(h[i]-h[j]); i++; res=res+(h[i]-h[j])*(h[i]-h[j]); j--; } } else { while(i!=j) { res=res+(h[i]-h[j])*(h[i]-h[j]); i++; res=res+(h[i]-h[j])*(h[i]-h[j]); j--; } } if(i==j-1) { res+=(h[i]-h[j])*(h[i]-h[j]); } cout<
P4447 分组
可以暴力枚举每个数加在哪个后面,但是是O(n2)的 用堆维护最小值可以到O(nlogn) 对于每个序列,如果接不上了就可以从堆中丢弃,并取得长度最小值,如果接的上就接上去。#includeusing namespace std;const int N=1e5+10;int a[N];priority_queue > pq;int minn=INT_MAX;int main(){ int n; cin>>n; for(int i=0;i >a[i]; } sort(a,a+n); for(int i=0;i 1-pq.top().first) { //cout< <<" "<<1-pq.top().first< < x=pq.top(); pq.pop(); x.second--; x.first--; pq.push(x); } while(!pq.empty()) { minn=min(minn,-pq.top().second); pq.pop(); } cout<
P1080 国王游戏
涉及难点:贪心性质的证明和高精度乘除法 对于贪心性质的证明:我们先看一题这道题的加减法版本 耍杂技的牛 这题是对w+s排序,也不需要用到高精度#include#include using namespace std;const int N=100010;pair a[N];int w;int s;int main(){ int n; cin>>n; for(int i=0;i >w>>s; a[i].first=w+s; a[i].second=s; } sort(a,a+n); int sum=0; int res=-100000000; for(int i=0;i
思路:见
简单的说就是交换两项,看看他们的最大值有没有变化。当然这个证明基于你已经有贪心思路的情况下。对于高精度套板就行
乘法:令t=a[i]*b,把t%10的结果留下来,把t/10的结果进位 除法:与列竖式一样的,不断借位 最后如果有多余的进位也放进vector里,把t/b留下来,t%b继续借位除 注意把结果反过来pop前导零,或者做一个标记标记前导零另外,我们直接用的反向的数字vector进行操作,要注意各个模板的变化。
#includeusing namespace std;const int N=1010;int a[N];int b[N];pair p[N]; vector mul(vector a,int b){ vector v; int t=0; for(int i=0;i
Acwing
区间选点
对于区间的贪心,不妨试一下,左端排序,右端排序,双端排序等等 最大不相交区间数量#include#include #include using namespace std;pair p;vector > v;int main(){ int n; cin>>n; int k=n; while(n--) { int l,r; cin>>l>>r; p.second=l; p.first=r; v.push_back(p); } sort(v.begin(),v.end()); int tot=1; int pos=v[0].first; for(int i=1;i pos) { tot++; pos=v[i].first; } } cout<
区间分组
#include#include #include #include using namespace std;pair p;vector > v;priority_queue ,greater > q;int main(){ int n; cin>>n; int k=n; while(n--) { int l,r; cin>>l>>r; p.first=l; p.second=r; v.push_back(p); } sort(v.begin(),v.end()); int pos=v[0].first; for(int i=0;i =v[i].first) { q.push(v[i].second); } else { q.push(max(q.top(),v[i].second)); q.pop(); } } cout<
区间覆盖
#include#include using namespace std;const int N = 100010;int n;struct Range{ int l, r; bool operator< (const Range &W)const { return l < W.l; }}range[N];int main(){ int st, ed; scanf("%d%d", &st, &ed); scanf("%d", &n); for (int i = 0; i < n; i ++ ) { int l, r; scanf("%d%d", &l, &r); range[i] = { l, r}; } sort(range, range + n); int res = 0; bool success = false; for (int i = 0; i < n; i ++ ) { int j = i, r = -2e9; while (j < n && range[j].l <= st) { r = max(r, range[j].r); j ++ ; } if (r < st) { res = -1; break; } res ++ ; if (r >= ed) { success = true; break; } st = r; i = j - 1; } if (!success) res = -1; printf("%d\n", res); return 0;}
合并果子
#include#include using namespace std;const int N=2e5+10;priority_queue ,greater > p;int main(){ int n; cin>>n; while(n--) { int x; cin>>x; p.push(x); } int res=0; while(p.size()>1) { int a=p.top(); p.pop(); int b=p.top(); p.pop(); p.push(a+b); res+=a+b; } cout<
货舱选址
#include#include using namespace std;const int N = 100010;int n;int q[N];int main(){ scanf("%d", &n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); sort(q, q + n); int res = 0; for (int i = 0; i < n; i ++ ) res += abs(q[i] - q[n / 2]); printf("%d\n", res); return 0;}
耍杂技的牛
#include#include using namespace std;const int N=100010;pair a[N];int w;int s;int main(){ int n; cin>>n; for(int i=0;i >w>>s; a[i].first=w+s; a[i].second=s; } sort(a,a+n); int sum=0; int res=-100000000; for(int i=0;i
算法竞赛进阶指南(还没做,之后更)
POJ 3614 SunScreen转载地址:http://dyvx.baihongyu.com/