贪心算法
(无模板)
区间问题:
例题:区间选点
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n;
struct node{
int l,r;
const bool operator < (const node &a) const{
return r<a.r;
}
}eg[N];
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
int a,b;
cin >> a >> b;
eg[i]={a,b};
}
sort(eg,eg+n);
int res=0,ed=-2e9; //取一个比所有的值都要小的数,使能取到第一条线段
for(int i=0;i<n;i++)
{
if(eg[i].l>ed){
res++;
ed=eg[i].r;
}
}
cout << res << endl;
return 0;
}
按右端点排序,每次找前一个线段的左端点严格小于下一个的右端点的线段,得到的所有线段都是严格没有相交部分的,也就是互相独立的部分,每一部分可能有不同数量的线段组成,严格独立的部分的数量就是 最少的选点的数量。
例题:最大不相交区间数量
给定 N N N个闭区间 [ a i , b i ] [ai,bi] [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
思路代码同上
例题:区间分组
(感觉稍稍有点难,不太好想,没想到用到优先队列,还有里面的相关处理)
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+10;
int n;
struct node{
int l,r;
const bool operator < (const node&a) const {
return l<a.l;
}
}eg[N];
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
int a,b;
cin >> a >> b;
eg[i]={a,b};
}
sort(eg,eg+n);
priority_queue<int,vector<int>,greater<int>>pq;//小根堆
for(int i=0;i<n;i++)
{
auto t=eg[i];
if(pq.empty()||pq.top()>=t.l) pq.push(t.r);//如果当前集合内的右端点的最小值与当前线段有重合,那么把当前集合的右端点加进集合
//也说明组数加一
else{//否则说明 可以在同一组 那么组合数不需要再加一,而是把这个可以相容的组合的信息更新,即把右端点更新
pq.pop();
pq.push(t.r);
}
}
cout << pq.size() << endl;
return 0;
}
例题:区间覆盖
(思想与上一题相似)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n,st,ed;
struct node{
int l,r;
const bool operator<(const node&a)
{
return l<a.l;
}
}eg[N];
int main()
{
cin >> st >> ed >> n;
bool can=0;
for(int i=0;i<n;i++)
{
int a,b;
cin >> a >> b;
eg[i]={a,b};
}
sort(eg,eg+n);
int res=0;
for(int i=0;i<n;i++)
{
int j=i,dd=-2e9;
while(j<n&&eg[j].l<=st){
dd=max(dd,eg[j].r);
j++;
}
if(dd<st) break;//若前一个区间右端点的最大值都小于下一个区间的左端点,那么一定无解
res++;//否则区间数加一
if(dd>=ed) {//如果此时所选区间右端点已经满足题意那么也直接结束
can=1;//标记一下是成功了才结束的
break;
}
st=dd;//更新区间右端点
i=j-1;//细节:while结束时j是第一个不满足条件的
}
if(!can) res=-1;
cout << res << endl;
return 0;
}
H u f f m a n Huffman Huffman树
例题:合并果子
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 10010;
int n;
int main()
{
cin >> n;
priority_queue<int,vector<int>,greater<int>>pq;
while(n--){
int a;
cin >> a;
pq.push(a);
}
int res=0;
while(pq.size()>1){
int a=pq.top();
res+=a,pq.pop();
int b=pq.top();
res+=b,pq.pop();
//res+=a+b;
pq.push(a+b);//注意这里不是将res加进去
}
cout << res << endl;
return 0;
}
排序不等式
例题:排队打水
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n;
int t[N];
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> t[i];
sort(t,t+n);
ll res=0;
for(int i=0;i<n;i++){
res+=t[i]*(n-i-1);
}
cout << res << endl;
return 0;
}
证明:
等待时间之和= t 1 × ( n − 1 ) + t 2 × ( n − 2 ) + ⋯ + ( t n − 1 × 1 + t n × 0 t_1\times (n-1)+t_2\times (n-2)+\dots + (t_{n-1}\times 1+t_n\times 0 t1×(n−1)+t2×(n−2)+⋯+(tn−1×1+tn×0
所以将排队打水的时间从小到大排序。。。
绝对值不等式
例题:仓库选址
(小学奥数题,选最中间的点)
#include <iostream>
#include <algorithm>
using namespace std;
using namespace std;
const int N = 1e5+10;
int a[N];
int main()
{
int n; cin >> n;
for(int i=0;i<n;i++) cin >> a[i];
sort(a,a+n);
int dis=0;
int mid=a[n/2];//中位点
for(int i=0;i<n;i++) dis+=abs(a[i]-mid);
cout << dis << endl;
return 0;
}
推公式
例题:耍杂技的牛
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e4+10;
typedef pair<int,int>PII;
PII p[N];
int main()
{
int n; cin >> n;
for(int i=0;i<n;i++)
{
int w,s;
cin >> w >> s;
p[i]={w+s,s};
}
sort(p,p+n);
int res=-2e9,sum=0;
for(int i=0;i<n;i++)
{
int s=p[i].second,w=p[i].first-s;
res=max(res,sum-s);
sum+=w;
}
cout << res << endl;
return 0;
}
把 w i + s i w_i+s_i wi+si从小到大排序,小的在上大的在下。
小结:
贪心算法…就是猜
难的主要是证明,好猜但不好证,或者说不好猜也不好证。。。