SDU程序设计作业Week3(贪心)

A-选数问题

题目:

题目
题意
本题输入n个数并从中选出K个数使得他们的和为S,我们需要求出一共有多少种方案!
Input&&Output:
Input&&Output
Sample:

#input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
#output
4

题解:

1.本题要求我们从一组数中选择一定个数的数使他们的和满足一定的条件,显然我们需要遍历这组数,一种比较直白的方式就是求出所有组合然后筛选出满足的组合,然鹅显然我们会求出许多不必要的解。
2.首先我们发现取三个数这三个数不能相同,因此当我们取下一个数时可以令他跟前面取过的数不重复。
3.接着思考,由于三个数的和小于S,我们可以思考一下如果我们挑出的第一个数就比S大显然我们不需要选择后两个数这种情况必定不满足,同理若前两个数之和大于S我们也不需要考虑他们的组合。因此我们想到了有小到大的遍历这组数,当遇到第一个数或者前两个数之和就大于S时我们可以不寻找他们之后的元素。
4.遍历数组时我们采用函数递归的方式并利用一个全局变量记录可选组数,并用一个参数表示取的是第几个数,当计数数组执行完毕后将会得出最终结果。

C++代码:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int group_num,n,K,S,cou = 0;
int sum = 0;
void num(vector<int> v,int index,int num1){
	for(int i = index;i<v.size();i++){
		sum+=v[i];
		if(sum>=S&&num!=1) return;
		if(num1 == 1){
			if(sum == S){
				cou++;
			}
			sum-=v[i];
			continue;
		}
		num1--;
		num(v,i+1,num1);
		num1++;
		sum-=v[i];
	}
	return;
}
int main(){
	cin>>group_num;
	for(int i = 0;i<group_num;i++){
		cin>>n>>K>>S;
		vector<int> v(n);
		for(int i = 0;i<v.size();i++) v[i] = 0;
		for(int i = 0;i<v.size();i++) cin>>v[i];
		sort(v.begin(),v.end());
		num(v,0,K);
		cout<<cou<<endl;
		cou = 0;
	}
	return 0;
} 

B-区间选点

题目:

题目
Input&&Output:
Input&&Output
Sample:

#Input 
2
1 5
4 6
#Output 
1
#Input 
3
1 3
2 5
4 6
#Output 
2

题解:

1.本题我们需要找最少数目的点并且可以保证所选区间内至少有一个点,那么显然每个区间只有一个点是最优的。
2.其次不同区间内的点可以是相同的因此我们可以这样想 若两个区间交集非空那么他们实际上可用一个区间表示,也就是他们的交集,若两个区间交集为空那么他们依然是两个区间,也就是说交集非空的两个区间只需要取一个点,交集为空的两个区间需要取两个点;因此我们可以对区间进行排序在这里我按照先b后a的顺序进行排序,我们可以发现这样排序之后前面的区间要么与后面区间交集为空要么是后面区间的子集,而小区间对大区间影响最大的值显然是它的右端点,我们可以一次更新,若出现右端点小于左端点,那么进入下一组区间,并更新判断值
PS:一组的理解:[a1[a2[a3 b3]b2]b1], [a4 b4] 这就是两组数据 ,我们排序过后 每组内最小区间的最大值一定大于等于这组区间的所有右端点  

C++代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int a,b,n;
struct interval{
	int a,b;
};
vector<interval> v(0);

bool cmp(interval &x,interval &x1){
	if(x.b!=x1.b) return x.b<x1.b;
	return x.a<x1.a;
}
int main(){
	cin>>n;
	for(int j = 0;j<n;j++){
		cin>>a>>b;
		interval c = {a,b};
		v.push_back(c);
	}
	sort(v.begin(),v.end(),cmp);
	int current_dot = v[0].b,cou = 1;//初始化选点 
	for(int i = 0;i<v.size();i++){
		if(current_dot<v[i].a){
			current_dot = v[i].b;
			cou++;
		}
	}
	cout<<cou<<endl;
}

区间覆盖

题目:

题目
Input&&Output:
Input&&Output
Sample:

#input
3 10
1 7
3 6
6 10

#output
2

题解:

1.区间覆盖问题采用贪婪方法就是我们每次取得区间都要使它的“权重”最高
2.比较简单的想法是我们找出所有组合然后选择能覆盖最少的统计使用区间数量
3.显然暴力方法时间损耗太多,那么我们可以这样想如果我们取得的区间中存在不相连的,或者所选区间的最小值大于下界或最大值小于上界那么这些方案显然是不可行的。这提示我们按一定的次序寻找会便于我们筛查,因此此处我们首先按照a的大小顺序将区间排序依次选择。
4.每次选择后实际上我们可以看作一个新的区间覆盖问题只不过下届改变了,可选区间变少了。
3..权重的衡量 首先加入这个区间我们需要保证区间覆盖是合法的,其次我们要让这个区间尽可能的覆盖更多的长度
4.合法判断:首先我们加入的区间的下界必须比剩余未覆盖区间的下界小(保证区间可以覆盖),其次加入区间的上界必须比未覆盖区间上界大(这样保证加入此区间有意义)
5.本题特殊性本题覆盖整点  也就是说 [1,2][3,4]这样的区间是可以覆盖[1,4]的,观察可以发现实际上对于区间下届a当a>2时 a与a+1在下届上是等效的,因此我们可以对他们做处理这样整点区间覆盖又变成了连续区间覆盖问题
7.初始化应选择从1开始的最长区间,若不存在则不可覆盖
6.之后以第一次更新为例:
我们首先观察第一个点若不合法那么区间无法覆盖,若合法首先选择它,并将new_s暂时更新为它的上界b,但此时我们还不应该更新选择区间的数量,因为当我们选择了区间之后那么对于后面区间的下届来说,只要他们在上一个区间内那么他们是等效的,我们需要取的是上界最大的,因此只有当我们所选区间的下一个的下届大于上一次区间的上界时才应该更新所选区间的数量以及新一轮的下届s。

C++代码:

#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

struct interval{
	int a,b;
};

bool cmp(const interval x1,const interval x){
	if(x1.a!=x.a) return x1.a<x.a;
	return x1.b-x1.a>x.b-x.a;
}
int a,b,N,T,cou = 0;
vector<interval> v(0);
int main(){
	scanf("%d%d",&N,&T);
	for(int i = 0;i<N;i++){
		scanf("%d%d",&a,&b);
		if(a>2) a -= 1;//对于a来说 从二开始 n与n+1在a的意义上等价 
		interval c = {a,b};
		v.push_back(c);
	}
	sort(v.begin(),v.end(),cmp);
	int s = 1;
	int new_s = 1;
	for(int i = 0;i<v.size();i++){
		if(v[i].a>s){
			cou = -1;
			break;
		}
		if(v[i].a <= s&&v[i].b>s&&v[i].b>new_s){
			new_s = v[i].b;
		}
		if((i+1==v.size()&&new_s!=s)||(i+1 != v.size()&&v[i+1].a>s&&new_s!=s){
			cou++;
			s = new_s;
		}
		if(s>=T) break;
	}
	if(s<T) cou = -1;
	printf("%d\n",cou);
	return 0;
} 

发布了7 篇原创文章 · 获赞 3 · 访问量 443

猜你喜欢

转载自blog.csdn.net/TIM__one/article/details/104718918
今日推荐