程序设计思维与实践 Week3 作业

A-选数问题

Given nn positive numbers, ZJM can select exactly KK of them that sums to SS. Now ZJM wonders how many ways to get it!

Input

The first line, an integer T<=100T<=100, indicates the number of test cases. For each case, there are two lines. The first line, three integers indicate nn, KK and SS. The second line, nn integers indicate the positive numbers.

Output

For each case, an integer indicate the answer in a independent line.

Example

在这里插入图片描述

Note

Remember that k<=n<=16k<=n<=16 and all numbers can be stored in 32-bit integer

思路:

这是一个子集枚举问题,可以枚举所有子集判断是否合法,显然不能枚举所有情况,但是首先可以确定基本思路为递归。

int func(int *str,int cur,int n,int k,int s,int sum){
//str为所有数的数组,cur为当前数组的位置.n为数组长度,k为要选的数的个数,s为要选出的数字之和,sum为当前所选数字之和
......
}

在枚举的过程中,要减少不必要的枚举,就要进行可行性剪枝,如果当前的情况不满足要求就不必再往下递归,具体是
如果当前数组的位置超过数组长度
如果要选的数字个数小于0
如果当前所选数组之和大于要选出数字之和
(数组在传入前需要从小到大排序)
就没有必要往下递归

if(cur>n || k<0 || sum>s)
	return 0;

还需要判断递归结束的条件,即当前所选数字之和等于要选出数字之和,并且不需要再挑选数字

if(sum==s && k==0)
	return 1;

对于每个数字,有两种情况:选或不选,分别展开递归

int temp1=func(str,cur+1,n,k-1,s,str[cur]+sum);
int temp2=func(str,cur+1,n,k,s,sum);
 return temp1+temp2;

总的递归函数为:

int func(int *str,int cur,int n,int k,int s,int sum){

	if(cur>n || k<0 || sum>s)
		return 0;
	if(sum==s && k==0)
		return 1;
	int temp1=func(str,cur+1,n,k-1,s,str[cur]+sum);
	int temp2=func(str,cur+1,n,k,s,sum);
	return temp1+temp2;
	
}

代码:

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

int func(int *str,int cur,int n,int k,int s,int sum){

	if(cur>n || k<0 || sum>s)
		return 0;
	if(sum==s && k==0)
		return 1;
	int temp1=func(str,cur+1,n,k-1,s,str[cur]+sum);
	int temp2=func(str,cur+1,n,k,s,sum);
	return temp1+temp2;
	
}

int main(){
	int T;
	scanf("%d",&T);
	for(int start=0;start<T;start++){
		int n,k,s;
		scanf("%d %d %d",&n,&k,&s);
		int str[1000];
		for(int i=0;i<n;i++)
			scanf("%d",str+i);
		sort(str,str+n);

		int temp=func(str,0,n,k,s,0);
		printf("%d\n",temp);

	}
	return 0;
	
}

B-区间选点

数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

扫描二维码关注公众号,回复: 9808722 查看本文章

Input

第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)

Output

一个整数,代表选点的数目

Examples

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

思路:

首先,如果大区间[a,b]包含小区间[m,n](a<m && b>n),那么,在小区间内任取一点,一定在大区间内。
其次,如果区间[a,b]与区间[m,n]有交集(不包含)(m>b || a>n),那么,在前面的区间内的最靠后(最大)的点一定在后面的区间内。

struct interval{
	int a=0,b=0;
	interval(int m,int n){
		if(m>n){
			swap(m,n);
		}
		a=m;
		b=n;
	}
	void show(){
		printf("[%d  %d]\n",a,b);
	}

	bool operator < (const interval& t){
		if(b!=t.b)
			return b<t.b;
		return a>t.a;
	}
};

因此,将所有区间按b的大小从小到大排序(b相同时按a的大小从大到小排序),贪心策略为取小区间的最后一个点,区间经过排序后小区间一定在前面,对于区间有交集的情况,选取前面小区间的最后一个点一定会保证该点一定落在后面的区间里,所选点数没有增加反而减少,这就是正确的贪心策略。

	int result=0;
	int lastEnd=-100000;
	for(int i=0;i<s.size();i++){
		interval temp=s[i];
//		temp.show();
		if(lastEnd<temp.a){
			result++;
			lastEnd=temp.b;
		}
	}

代码:

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

struct interval{
	int a=0,b=0;
	interval(int m,int n){
		if(m>n){
			swap(m,n);
		}
		a=m;
		b=n;
	}
	void show(){
		printf("[%d  %d]\n",a,b);
	}

	bool operator < (const interval& t){
		if(b!=t.b)
			return b<t.b;
		return a>t.a;
	}
};

int main(){
	int n;
	scanf("%d",&n);
	vector<interval> s;
	for(int i=0;i<n;i++){
		int tempa,tempb;
		scanf("%d %d",&tempa,&tempb);
		s.push_back(interval(tempa,tempb));
	}
	
	sort(s.begin(),s.end());
//	for(int i=0;i<s.size();i++){
//		s[i].show();
//	}
//	cout<<endl<<endl;
	int result=0;
	int lastEnd=-100000;
	for(int i=0;i<s.size();i++){
		interval temp=s[i];
//		temp.show();
		if(lastEnd<temp.a){
			result++;
			lastEnd=temp.b;
		}
	}

	printf("%d\n",result);	
	return 0;
}

C-区间覆盖

描述:

数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1

输入:

第一行:N和T
第二行至N+1行: 每一行一个闭区间

输出:

选择的区间的数目,不可能办到输出-1

样例输入:

3 10
1 7
3 6
6 10

样例输出:

2

提示:

这道题输入数据很多,请用scanf而不是cin

思路:

首先,在一个区间的有序序列(先按左端点从小到大排序,左断点相同时按右端点从大到小排序),对于相邻的两个区间,有两种情况:

struct interval{
	int a,b;
	interval(int m,int n){
		a=m;
		b=n;
	}
	void show(){
		printf("%d %d\n",a,b);
	}
	bool operator < (const interval& t)const {
		if(a!=t.a)
			return a<t.a;
		return b>t.b;
	}
};

1、后一段区间包含前一段区间的开头,此时尽量选择后一段区间中末尾最大的那个区间

if(s[i].a <=start) 
	end=s[i].b>end?s[i].b:end;//包含当前区间的最长的一条线段 

2、后一段区间不包含前一段区间的开头,此时有可以分为两种情况:
(1)、后一段区间的开头在前一段区间中,此时后一段区间合法,并且更新start和end

start=end+1;//更新后一段区间的最大开始位置
if(s[i].a <= start) 
	end=s[i].b;//下一条线段与最长线段有交集 

(2)、后一段区间的开头在前一段区间末尾的后面,此时后一段区间与前一段区间之间有区间没有覆盖掉,因此不可以办到

start=end+1;//更新后一段区间的最大开始位置
if(s[i].a > start){
	printf("-1");return 0;
}

退出循环的情况有两个:
1、如果区间的末尾已经到指定覆盖区间的末尾,成功

if(end==t){
	printf("%d",result);return 0;
}

2、如果遍历完成了所有的区间还没有覆盖到区间的末尾,失败

for(int i=0;i<s.size();i++){
...}
printf("-1");	
return 0;

代码:

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

struct interval{
	int a,b;
	interval(int m,int n){
		a=m;
		b=n;
	}
	void show(){
		printf("%d %d\n",a,b);
	}
	bool operator < (const interval& t)const {
		if(a!=t.a)
			return a<t.a;
		return b>t.b;
	}
};

int main(){
	int n,t;
	vector<interval> s;
	scanf("%d %d",&n,&t);
	for(int i=0;i<n;i++){
		int a,b;
		scanf("%d %d",&a,&b);
		if(a>b) swap(a,b);
		if(b<1) continue;
		if(a<1) a=1;
		if(a>t) continue;
		if(b>t) t=b;
		s.push_back(interval(a,b));
	}
	sort(s.begin(),s.end());

	if(s[0].a!=1){
		printf("-1");
		return 0;
	}
	
	int start=1,end=s[0].b;
//	int lastEnd=1;
	int result=1;
	for(int i=0;i<s.size();i++){
		if(s[i].a <=start) end=s[i].b>end?s[i].b:end;//包含当前区间的最长的一条线段 
		
		//与当前区间 
		else{
			result++;
			start=end+1;
			if(s[i].a <= start) end=s[i].b;//下一条线段与最长线段有交集 
			
			else{
				printf("-1");return 0;
			} 
		}
		if(end==t){
			printf("%d",result);return 0;
		}
		
	}
	printf("-1");
	return 0;
}


发布了2 篇原创文章 · 获赞 0 · 访问量 26

猜你喜欢

转载自blog.csdn.net/weixin_44343319/article/details/104707186
今日推荐