[Acwing] 算法基础课总结 六 贪心

区间问题

1.区间选点

题意 :
给定多个区间,找到最少的点,使得每个区间至少包含一个点
思路 :
在这里插入图片描述
我们把相交区间分为上面大致三种

我们固定一个答案 R R R,表示当前贪心的右端点

显然如果按照左端点进行排序之后,绿色那种情况其实在枚举的时候,会转变为蓝色的那种情况

因此当两个区间不相交的时候 R = a [ i ] . r , + + r e s R=a[i].r,++res R=a[i].r,++res
否则当两个区间相交的时候,我们取较小的 R R R,因为这样子可以包含更多的区间

typedef priority_queue<int,vector<int>,greater<int>>  Pri_m;
typedef pair<int,int> pii;
typedef vector<int> VI;
map<int,int> mp;
const int N = 2e5+10,INF = 0x3f3f3f3f;
const double eps = 1e-5;
void solve(){
	vector<pii> v;
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int l,r;cin>>l>>r;
		v.pb({l,r});
	}
	sort(all(v));
	int res = 0;
	int R = -INF;
	for(auto u : v){
		if(u.x <= R){//当两个区间相交的时候
			R = min(R,u.y);
		}else{//当两个区间没有相交的时候
			++res;
			R = u.y;
		}
	}
	cout<<res<<endl;
}

2.最大不相交区间

给定 [ L , R ] [L,R] [L,R]寻找不相交区间的最大数量

思路 :
问题转换 :
最大的不相交区间数量
最小的相交区间数量
选择最多的点使得每个区间至少包含一个
因为上面一题是求最少的点使得每个区间都至少包含即变相的保证了最多的相交区间
因为我们反向考虑则最少的相交区间就是最多的点

因此我们只需要在上面那题,当两个区间相交的时候计算一下答案即可

最后输出 n − r e s n-res nres

code :

void solve(){
	vector<pii> v;
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int l,r;cin>>l>>r;
		v.pb({l,r});
	}
	sort(all(v));
	int res = 0;
	int R = -INF;
	for(auto u : v){
		if(u.x <= R){//当两个区间相交的时候
			++res;
			R = min(R,u.y);
		}else{//当两个区间没有相交的时候
			// ++res;
			R = u.y;
		}
	}
	cout<<n-res<<endl;
}
3.区间分组

题意 :
给定 N N N个区间,我们需要将这些区间分成若干组,使得每组内两个区间没有交集,同时还需要使得数组经可能小
输出最小组数

思路 :

code :

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100100;

int n;
int b[2 * N], idx;

int main()
{
    scanf ("%d", &n);
    for(int i = 0; i < n; i ++)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        b[idx ++] = l * 2;//标记左端点为偶数。
        b[idx ++] = r * 2 + 1;// 标记右端点为奇数。
    }

    sort(b, b + idx);

    int res = 1, t = 0;
    for(int i = 0; i < idx; i ++)
    {
        if(b[i] % 2 == 0) t ++;
        else t --;
        res = max(res, t);
    }
    printf ("%d\n", res);
    return 0;
}
4.区间覆盖

题意 :
给定 N N N个区间,以及一个 [ S , T ] [S,T] [S,T]选择尽量少的区间,将这个区间完全覆盖

思路 :
贪心思路
因为需要选择较少数量的区间,因此我们肯定能在可覆盖区间里面寻找一个右端点最大的区间

然后在用这个右端点更新我们的 s t st st

虽然思路好像不过实现不好实现

这里使用双指针找到合法区间,然后在中途进行中断

code :

void solve(){
	vector<pii> v;
	int L,R;cin>>L>>R;
	
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int a,b;cin>>a>>b;
		v.pb({a,b});
	}
	sort(all(v));
	
	int res = 0;
	
	for(int i = 0 ; i < n ; i ++ ){
		int j = i , r = -INF;
		
		while(j<n && v[j].x <= L){
			r = max(r,v[j].y);
			j++;
		}
		
		if(r < L){
			cout<<-1<<endl;
			return;//没有找到
		}
		
		++res;
		if(r>=R){
			cout<<res<<endl;
			return;
		}
		
		L = r;
		i = j-1;
	}
	cout<<-1<<endl;
	
}

Huffman树

1.合并果子

题意 :
经典

思路 :
显然的,我们不可能让大的数一直贡献价值,所以我们考虑使用小根堆,让小的尽可能一直贡献价值

但是这种题不能解决相邻合并的问题

code :

void solve(){
	priority_queue<int,vector<int>,greater<int>> heap;
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int x;cin>>x;
		heap.push(x);
	}
	
	ll res = 0 ;
	
	while(heap.size()>1){
		auto x = heap.top();
		heap.pop();
		auto y = heap.top();
		heap.pop();
		
		//cout<<x<<" "<<y<<endl;
		
		res += x+y;
		heap.push(x+y);
	}
	cout<<res<<endl;
	
}

排序不等式

1.排队打水

题意 :
1个水龙头 n n n个人排队大水,询问所有人的等待时间之和最小

思路 :
我们知道等待时间之和可以如下计算 :

a [ 1 ] ∗ ( n − 1 ) + . . a [ i ] ∗ ( n − i ) . . . a[1]*(n-1)+..a[i]*(n-i)... a[1](n1)+..a[i](ni)...

这个公式显然是很好懂的,也就是对于当前人的在打水就有 n − i n-i ni个人需要等 a [ i ] a[i] a[i]的时间

因为 i i i是不固定给定,所以我们可以给其排个序使得 最小的*最大的 因此即得

code :

void solve(){
	int n;cin>>n;
	vector<int> v;
	
	for(int i=1;i<=n;i++){
		int x;cin>>x;
		v.pb(x);
	}
	
	sort(all(v));
	ll res = 0 ;
	int cnt =  0;
	
	for(auto x : v){
		res += (x*(n - cnt- 1));
		++cnt;
	}
	cout<<res<<endl;
	
}

绝对值不等式

1.货仓选址

题意 :
两头商家往一个点跑,询问最小话费

思路 :
我们假设选的为 x x x

a b s ( w [ 1 ] − x ) + a b s ( w [ 2 ] − x ) . . . . + a b s ( w [ n ] − x ) abs(w[1]-x)+abs(w[2]-x)....+abs(w[n]-x) abs(w[1]x)+abs(w[2]x)....+abs(w[n]x)

显然的当且仅当我们选取中位数的时候取得 最小值

code :

void solve(){
	vector<int> v;
	int n;cin>>n;
	for(int i=1;i<=n;i++){
		int x;cin>>x;
		v.pb(x);
	} 
	sort(all(v));
	int mid = v.size()/2;
	ll res  = 0 ;
	
	for(int i=0;i<n;i++){
		res += abs(v[mid] - v[i]);
	}
	cout<<res<<endl;
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值