2.24 算法练习

本文详细探讨了两种不同的算法问题:区间选点和区间分组。对于区间选点,通过分析不同情况,得出了解决方案,特别是针对价格不能被3整除的情况。对于区间分组,文章首先介绍了作者自己的尝试,然后展示了ACWing的高效解决方案,使用小顶堆实现了快速分组。最后,作者提到了一种将区间分组问题转化为活动安排问题的巧妙思路,并给出了相应代码实现。
摘要由CSDN通过智能技术生成

藏匿的刺客
藏匿的刺客

用时 1min 得分100
因为我发现这个其实就是区间选点,果然acwing很不错
代码就不提交了

幸运的店家
幸运的店家

看了题解,但是不太明白,似懂非懂的

/*
问题描述
  炫炫开了一家商店,卖的货只有一个,XXX,XXX卖N元钱。
	有趣的是,世界上只有面值为3的幂的纸币,即纸币只有1元的、3元的、9元的。。。。,
	有一天,桥神来买XXX,可他没办法正好给出N元钱,而炫炫没法找零,于是他只好用他的钱凑出了一个比N大,
	并且最小的价值,交给了炫炫。炫炫想知道,他这次最多可以得到多少张纸币。
输入格式
  一个数,N
输出格式
  一个数,为答案
样例输入
4
样例输出
2
数据规模和约定
  n<=10^17
*/


/*
答案必不使用1,若需要使用若干1元,得到x,满足x > N。
那么去掉1元,得到x1,可能满足x1 >= N,当x1 > N,可知x不是最小,当x1 = N,不符合题意。	
*/ 

#include<iostream>

using namespace std;

long long n;

int main(){
	cin>>n;
	
	if(n<=3){
		//如果价格为1,2,3.如果凑不成恰好的,则都需要一枚货币(分别为3,3,9元)
		cout<<1<<endl; 
	}
	
	//如果价格不能被3整除,那么必然需要n/3+1个,就是全用3 
	//如果可以被3整除
	/*
		之前我是想尽可能使用3,到最后使用一个6但是
		9-->3+3+6
		这样的话直接可以3+6,不满足题意
		
		那么这样的用3的n次幂组成
		
		若N能被3整除,则答案是n / 3 + 1(将N的所有3因子除掉后得到n,即n满足 3^k * n = N )
		如果不把 所有3因子 都除掉,那么最终总会可以凑出整钱 
	*/ 
	
	else{
		//对于3的倍数,要确保 “可他没办法正好给出N元钱”
		//6:一张3,一张6 9:两张3,一张6 规律应该是x/3即张数 
		if(n%3==0){
			while (n%3==0)	n/=3;
			cout<<n/3+1<<endl;
		}
		else{
			cout<<n/3+1<<endl;
		}
	}
	
	
	return 0;
}

区间分组
区间分组
方法一和二

方法一是我写的,应该是对的,不过最后超时了,确实时间复杂度太高
方法二是Acwing的方法,很巧妙地用了小顶堆
小顶堆的构造代码为priority_queue<int, vector<int>, greater<int>> heap;//构造一个小顶堆,greater表示顶堆元素最小这个priority_queue其实是优先队列,如果构造大顶堆就是priority_queue<int, vector<int>, less<int>> heap

本题贪心不难想,不能用右端点排序的数据为
[1,3][2,5][4,100][10,13]

代码

/*
给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,
并使得组数尽可能小。

输出最小组数。

输入格式
第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示最小组数。

数据范围
1≤N≤10^5,
-10^9≤ai≤bi≤10^9
输入样例:
3
-1 1
2 4
3 5
输出样例:
2

*/


//试了几个,感觉左右端点都行,因为我想要某个离当前区间最近的左端点,因此这次左端点升序,如果左一样,则右升序

#include<iostream>
#include<algorithm>
#include<list>
#include<vector>
#include <queue>

using namespace std;

const int N=1e5+10;

struct node{
	int a,b;//左右端点 
	int e=0;
};

node q[N];

bool cmp(node x,node y){
	if(x.a==y.a)
		return x.b<y.b;
	
	return x.a<y.a;
}

int main(){
	int n;
	cin>>n;
	
	for(int i=1;i<=n;i++)
		cin>>q[i].a>>q[i].b;
	
	int ans=0;
	int sorted=0;
	node m;
	node ini;
	ini.a=0;ini.b=0;
	
	sort(q+1,q+1+n,cmp);
	
	
	//利用这个算法会超时 
	//e是判断是否进组了,后续改代码的时候我就没删除它 
	/* 
	while(sorted!=n){
		ans++;
		//当所有区间都分组了才退出循环
		for(int i=1;i<=n;i++){
			if(q[i].e==0){
				m=q[i];//每次开头都会选择一个区间作为新的组的第一个元素 
				q[i].e=1;//说明该区间已经排序了
				sorted++; 
				break;
			}
		}
		int i=1;
		while(i<=n){
			if(q[i].e==0&&q[i].a>m.b){
				//如果某个区间没有用过且不相交,并且由于按照左端点排序了,因此是当前最优的区间 
				m=q[i]; 
				q[i].e=1;
				sorted++;
			}
			i++;
		}
	}
	*/
	
	//下面是Acwing做法 
	priority_queue<int, vector<int>, greater<int>> heap;//构造一个小顶堆,greater表示顶堆元素最小 
	//堆内某个元素是某个分组内最大的右端点 
	//用一个元素代表区间的那个分组 
		
	for(int i=1;i<=n;i++){
		node m=q[i];
		//如果堆是空的(一开始)或者当前区间的左端点比任何一个分组右端点的最小值还小,那就得重新分入一个区间 
		if(heap.empty()||m.a<=heap.top()){
			heap.push(m.b);	
		} 	
		
		else{//如果不是,那么可以入堆,而且组要更新右端点 
			//尽量选择和右端点最小的那个一组(贪心的地方)也就是我原本思路中“选择可以的且离它最近的” 
			heap.pop();
			heap.push(m.b);
		}
	
	}
	//堆的元素个数就是组数 
	
	cout<<heap.size()<<endl;
	
	return 0;
} 



 

方法三

在看题解的时候,发现了一个十分厉害的思路:

把这个问题想象成活动安排问题

有若干个活动,第i个活动开始时间和结束时间是[a,b],同一个教室安排的活动之间不能交叠,求要安排所有活动,少需要几个教室?

有时间冲突的活动不能安排在同一间教室,与该问题的限制条件相同,即最小需要的教室个数即为该题答案。

我们可以把所有开始时间和结束时间排序,遇到开始时间就把需要的教室加1,遇到结束时间就把需要的教室减1,在一系列需要的教室个数变化的过程中,峰值就是多同时进行的活动数,也是我们至少需要的教室数。

代码

太厉害了啊是😭

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1e5+10;

int a[2*N];

int main(){
	int n;
	cin>>n;
	int l,r;
	
	for(int i=1;i<=n;i++){
		cin>>l>>r;
		a[i*2-1]=l*2-1;
		a[i*2]=r*2;//将所有端点映射到一维数组中 
	}
	
	sort(a+1,a+1+2*n);
	
	int ans=0;
	int tmp=0;
	
	for(int i=1;i<=2*n;i++){
		if(a[i]%2){//如果是左端点,表明有活动开始,需要一间教室
			tmp++;
			ans=max(ans,tmp); 
		}
		else{
			tmp--;
		}
			
	}
	
	cout<<ans;
	
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值