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!InputThe 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.OutputFor each case, an integer indicate the answer in a independent line.
Example
Input
1
10 3 10
1 2 3 4 5 6 7 8 9 10
Output
4
Note:Remember that k<=n<=16k<=n<=16 and all numbers can be stored in 32-bit integer
思路分析:
选数问题是关于集合找子集问题的一种变形,只不过是再找子集过程中添加了筛选条件,其中从中选固定的数,并且数的总和为已知定值。显然,对于寻找子集问题,我们会选择递归,不过这道题我们要注意在恰当的位置及时弹出。弹出条件有四个;
1:满足题意;2:所有元素考虑完毕;3:代码中所传vector的size大于k;4:S<0;
对于代码,其中值得思考的是对于vector的使用,STL容器对于处理定值问题确实是一个很好的选择(如果还需要访问其中的元素的话),但是这道题显得没有必要,我们可以选择使用int型的全局变量,这样会减少时间与空间消耗。另外,对于总和的问题,以下代码是利用与0进行比较的,这样的处理比起利用传递两个变量,比较两个变量是否相等要简便。

#include<iostream>
#include<vector>
using namespace std;
int ans=0;
void func(int S,int k,int i,vector<int>& a,int* b,int n)
{
	if(S==0&&a.size()==k)
	{
		ans++;
	    return;
	}
	if(i==n)
	return;
	if(a.size()>k||S<0)
	return;
	a.push_back(b[i]);
	func(S-b[i],k,i+1,a,b,n);
	a.pop_back();
	func(S,k,i+1,a,b,n);
}
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	{
		int N,K,S;
		cin>>N>>K>>S;
		int* b=new int[N];
		for(int i=0;i<N;i++)
		{
			cin>>b[i];
		}
		ans=0;
		vector<int> a;
		func(S,K,0,a,b,N);
		cout<<ans<<endl;
		delete[] b;
	}
	
}在这里插入代码片

题目二:B - 区间选点;
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)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
思路分析:
区间选点问题是对于贪心思想的一种运用,在逐步处理过程中,时刻在满足条件的情况下为下一步选择创造最有力的条件;对于这个题目,我们可以利用两种思路进行,其都是贪心策略,但是其出发点与预处理不太一样,但本质是一样的。
第一种:我们将每一个区间按照其右边界按照从小到大排序,若右边界相同,则按照左边界从大到小排序;这样在遍历的时候可以保证右边界相同的区间中长度最短的会先出现,也因为其会被其他区间包含,故也将其作为标准。接下来只需考虑其右边界是否大于等于其他区间的左边界。依次遍历,循环即可。
第二种:我们将每一个区间按照左边界升序右边界升序排序,在接下来的循环过程中,我们设置两个变量始终维护并更新上一个点可能选择的区间范围,当考虑节点的左区间不在该范围内,则点数加一;
总结:其实对于第一种思路与第二种思路的本质都是时刻更新上一个选点的范围,当范围缩小为零则点数加一;但是显然可以看出,第一种的预处理比较好,其条理更清晰,因为选点范围的一端已经固定,过程中需要维护与更新的变量更少;
代码一:

#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
	int a,b;
	bool operator< (const node& p)const
	{
		return b==p.b? a>p.a:b<p.b;
	} 
};
int main()
{
	int N;
	cin>>N;
	node* no=new node[N];
	for(int i=0;i<N;i++)
	{
		cin>>no[i].a>>no[i].b;
	}
	sort(no,no+N);
	int ans=1;
	int func=no[0].b;
	for(int i=1;i<N;i++)
	{
		if(no[i].a>func)
		{
			ans++;
			func=no[i].b;
		}
	}
	cout<<ans<<endl;
}在这里插入代码片

代码二:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{
	int a,b;
	node(){
	}
	bool operator<(const node& p)const
	{
		return a==p.a? b<p.b : a<p.a; 
	}
};
void func(node* no,int n)
{
	int ans=1;
	sort(no,no+n);
	int x=no[0].a;//始终维护上一次加入的点的可能区间; 
	int y=no[0].b;
	for(int i=1;i<n;i++)
	{
		int x1=no[i].a;
		int y1=no[i].b;
		if(x1>y)
		{
			ans++;
			x=x1;
			y=y1;
		}
		if(x<=x1&&x1<=y)
		{
		    x=x1;y=min(y,y1);	
		} 
	}
	cout<<ans<<endl;	
}
int main()
{
	int N;
	cin>>N;
	node* no=new node[N];
	for(int i=0;i<N;i++)
	{
		int a,b;
		cin>>no[i].a>>no[i].b;
	}
	func(no,N);
}在这里插入代码片

实验三: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;
思路分析:
这个题目也是利用贪心策略,不过这道题,自己写代码时WA了20次,这道题的思路比较简单,但是我感觉写出完整正确的代码需要的是清晰规整统一的思路,减少特判中的情况,比如这道题中,我们的代码中会含有若干变量,但我们要注意判断是否覆盖完毕时,最好将判断条件规整在一个变量中,在以下代码中,我们将判断条件赋予到lasty上,这种处理会清晰思路,另外就是lasty中保留的是上一次已经确定加的区间的右边界,这种确定性的处理是写代码时需要格外注意的,可以帮助我们规整思路,便于接下来的处理与讨论。
另外这个题有几个注意点:
1.闭区间的覆盖,即(1.2)与(3,4)是能够覆盖(1,4)的,这也就使得我们代码中会存在+1处理。
2.lastx,lastyans要初始为0,不能够为1.
易错数据1:
10 805

0 334
0 448
1 6
1 114
2 989
3 314
3 798
4 391
5 286
5 446
易错数据2:
2 1
-2 -5
-2 -4

#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdio.h>
using namespace std;
struct node {
	int a,b;
	node() {
	}
	bool operator<(const node& p)const {
		return a==p.a? b<p.b:a<p.a;
	}
};
void func(node* no,int n,int end) {
	sort(no,no+n);
	int ans=0;
	int lastx=0,lasty=0;
	int findx=0;
	int findy=0;
	for(int i=0; i<n; i++) {
		if(findy+1<no[i].a) {
			cout<<-1<<endl;
			return;
		}
		if(no[i].a>lasty+1) {
			ans++;
			lastx=findx;
			lasty=findy;
			if(lasty>=end) {
				cout<<ans<<endl;
				return;
			}
			if(no[i].b>findy) {
				findx=no[i].a;
				findy=no[i].b;
			}
		} else if(no[i].a<=lasty+1&&no[i].b>findy) {
			findx=no[i].a;
			findy=no[i].b;
		}
	}
	if(findy>=end) {
		cout<<ans+1<<endl;
		return;
	}
	cout<<-1<<endl;
	return;
}
int main() {
	int N,T;
	cin>>N>>T;
	node* no=new node[N];
	for(int i=0; i<N; i++) {
		scanf("%d%d",&no[i].a,&no[i].b);
	}
	func(no,N,T);
}在这里插入代码片
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值