week3-作业

A - 选数问题

题意:

给定n个正数,从中选出K个使这些数的和为S,一共有多少种选法。


思路:

这道题首先使用DFS深度优先搜索,也就是从起点开始向后探索,一旦发现原来的选择不符合要求,就回溯重新选择另外一点,反复探索,求得选中的K所有和为S的个数。
这就是说从第一个数开始,遍历出所有的数,每个数都有选中和未选中两种情况,需要利用递归来解决。在递归的时候剪枝,就能够大大降低算法的复杂度。当判断出和为S的时候,个数就+1。一直到遍历完所有的选择或者选中的个数>K的时候就会进行剪枝。

代码:

#include<iostream>
#include<list>
#include<string>
#include<algorithm>
using namespace std;
#define _for(i,a,b) for(int i=a;i<b;i++)
int T, n, K, S;
int m[16];
list<int> res;
int step=0;

void solve(int i, int K,int sum)
{
	if (res.size() == K && sum == 0) 
	{
		step++;
		return;
	}
	if (i >= n) return;
	if (res.size() > K || sum < 0) return;
	solve(i + 1, K, sum); //选中,放入链表中
	res.push_back(m[i]);
	solve(i + 1, K, sum - m[i]); //不选
	res.pop_back(); //不选的话,将该数从链表里弹出删除

}
int main()
{
	cin >> T;
	_for(i, 0, T)
	{
		cin >> n >> K >> S;
		_for(j, 0, n)
		{
			cin >> m[j];
		}
		solve(0, K, S);
		cout << step << endl;
		step = 0;
	}
	return 0;
}

B - 区间选点

题意:

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

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

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

思路:

首先需要找到贪心指标,可以看到应该逐渐判断右端点值,就能完成我们需要的目的。那就应该将所有区间依据右端点b从小到大排序,从排序好的第一个区间右端点为最大右端点同时开始判断,如果第一个区间的右端点小于后一个区间的左端点,那么就必须多取一个点,将该区间的右端点覆盖最大右端点,在进行下一个区间的判断。如果大于等于,就说明该右端点在该区间里,再利用当前右端点再对下一个区间的左端点进行比较,直到比较完所有的区间。所统计来的数目就是我们想要的值。
左右区间可以用结构体来表示。
因为一开始考虑的不严谨,使得判断第一个区间的右端点小于等于后一个区间的左端点时频繁wa,一些小的细节也仍需注意。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define _for(i, a, b) for(int i=a;i<b;i++)


struct section
{
	int a;
	int b;
	bool operator < (const section& s)const
	{
		if(b!= s.b)
			return b <s.b ;
		else
			return a > s.a;
	}
}s[1000];

bool cmp(const int &s1 ,const int &s2)
{
	return s1>s2 ;
}
int main()
{
	int N;
	cin >> N;
	int end=-1;
	int count = 0;
	_for(i, 0, N)
	{
		cin >> s[i].a >> s[i].b;
	}
	sort(s, s + N);
	_for(i, 0, N)
	{	
		if (end< s[i].a)
		{
			end=s[i].b;
			count++;
		}
	}
	cout << count << endl;
	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。


思路:

这道题疯狂打脸,稍微看了一下题就去做,未能考虑(1,2)(3,4)可以覆盖(1,4)这种情况,以致于频繁的wa。虽然试了很多样例,但还是错误,求助于同学才给我指点出来。读题一定要细心细心细心。
区间利用结构体表示。
首先还是需要找到合适的贪心指标,可以看到区间从1-t,如果所有区间的起点不是1则不可能办到。也就是将闭区间左端点从小到大排序,然后对所有区间进行遍历,选择起点为1的最长区间,将其右端点+1 设置成起点,然后重复以上过程及就能得到想要结果。


代码:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std; 
#define _for(i,a,b) for(int i=a;i<b;i++)

struct section
{
	int a;
	int b;
	bool operator <(const section &s1) 
	{
		if(a!=s1.a) return a<s1.a;
		else return b>s1.b; 
	}
	
}s[25001];

int main()
{
	int N, T,a,b;
	int count=1; //计数所用	
	scanf("%d %d", &N, &T);
	_for(i, 0, N)
	{
		scanf("%d %d", &a, &b);
		if(a<1) s[i].a=1;  //防止输入错误情况(考虑多了) 
		else s[i].a=a;
		if(b>T) s[i].b=T;
		else s[i].b=b;
	}	
	sort(s, s+ N);
	int maxb=s[0].b;
	int start=1;  //起点设置为1 
	if(s[0].a!=1)  //第一个区间起点不是1,则无解 
	{
		cout<<-1<<endl;
		return 0;
	}
	 
	_for(i,0,N)
	{
		if(s[i].a<=start)  //左端点小于等于起始点 
			maxb=max(maxb,s[i].b); //选择最大的右端点 
		else
		{
			count++; //否则那就需要多取一个区间 
			start=maxb+1;   //将起点设置为最大右端点+1 
			if(s[i].a<=start)   
				maxb=s[i].b;
			else
			{
				cout<<-1<<endl;
				return 0;
			}
			
		}
		if(maxb==T) //到达T 
		{
			cout<<count<<endl;
			return 0;
		}
		
	}
	cout<<-1<<endl;
	return 0;
}
发布了7 篇原创文章 · 获赞 0 · 访问量 124

猜你喜欢

转载自blog.csdn.net/weixin_44465341/article/details/104980551
今日推荐