SDU week 3作业--DFS与贪心

写在前面:

https://blog.csdn.net/effective_coder/article/details/8736718

这次实验的后两个题均选择贪心求解,而这是一篇对贪心算法讲的比较详细的博客,写在前面以便之后浏览。

选数问题

题目:
给定n个正数,其中选取K 个数使他们的和为S,怎样选?

Input:
T组数,其中T<=100;对于每一种情况,都有两行,第一行为数据量n,要选取K 个数,和为S,第二行,n个正数。

output:
对于每种情况,输出一个整数表示可行解的个数 。

思路:
基本思路就是可以枚举,和求子集的枚举方法类似。
在一个组合内,每一个数都是加入或者不加入两种选择。

fun(i+1,Sum,res);  //不选 
	
res.push_back(a[i]);  //选
fun(i+1,Sum-a[i],res);
res.pop_back();	 

每一次都是不选或者选两种情况,如果不选的话,就直接i+1,sum 不变;
如果选的话,就是将当前元素加入到列表t 种,同时需要注意的是,在下面要有一个pop_back,以便将最后一个元素删除,得到更多的组合。

需要注意的是终点的判断:
满足情况的是:res.size()==K && Sum==0
需要提前终止的是:res.size()> K||Sum<0
越界的是:i>=n

代码:

//选数问题 k<=n<=16  n个数,k个元素,总和是sum 
#include<stdio.h>
#include<list> 
#include<iostream>
using namespace std;
int K,n,tol=0;
int a[20];
void fun(int i,int Sum,list<int> &res)
{
	if(res.size()==K && Sum==0){
		tol++;     //达到终点 
		/*	for(list<int>::iterator iter=res.begin();iter!=res.end();iter++){
			cout<< *iter <<" ";
		}
		cout<<endl; */
		return ;
	}
	if(i>=n) return ;
	if(res.size()> K||Sum<0) 	return ;
	
	fun(i+1,Sum,res);  //不选 
	
	res.push_back(a[i]);  //选
	fun(i+1,Sum-a[i],res);
	res.pop_back();	 
}
int main()
{
	int T,Sum;
	cin>>T;
	list<int> res;
	for(int i=0;i<T;i++) 
	{
		cin>>n>>K>>Sum;
		for(int i=0;i<n;i++)
			cin>>a[i];
		int j=0; 
		fun(j,Sum,res);
		printf("%d\n",tol);
		res.clear();
		tol=0;
	}
 } 

区间选点问题

题目:
数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
Input:
第一行1个整数N(N<=100)
第2~N+1行,每行两个整数a,b(a,b<=100)
Output:
一个整数,代表选点的数目
思路:
这个题目是在区间内选择尽量少的点,考虑用贪心求解,主要问题仍然是贪心准则,选最少的点覆盖所有区间。首先能想到的是假如存在区间[a,b],那么对任何的区间[c,d] (c<b),总能在区间[a,b]中选一个点,满足这些区间的要求。这样就是按照区间右端点来考虑,因此我们需要在处理前对区间尽心排序,排序的准则就是

bool compare(section s,section e)
{
	if(s.y<e.y)		return s.y<e.y;    
	else return s.x<e.x;
}

而当s[i].x>now.y 则表示需要选择新的点了,此时count++;
这样的得出来的结果就是最优解。

代码:

//区间选点问题 
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct section{
	int x; 
	int y;
	bool operator < (const section &e)const{
		if(y!=e.y)	return y<e.y;
 		if(x!=e.x)	return x<e.x;
	}
};
void fun(section *s,int n)
{
	section now=s[0];
	int	count=1;
	for(int i=1;i<n;i++)
	{
		if(s[i].x>now.y){
			now=s[i];
			count++;
		}
	}
	cout<<count<<endl;
}
int main()
{
	int n;
	cin>>n;
	section *s= new section[n];
	for(int i=0;i<n;i++) {
		cin>>s[i].x>>s[i].y;
	}
	sort(s,s+n);
	fun(s,n);
 } 

区间覆盖问题

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

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

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

思路:
这个首先要思考的问题就是怎么选取区间,这就容易使人想到贪心准则,对当前的每一步都选取能获得最大利益的。
据此得到的贪心准则如下:
假设当前选取的区间是[a,b]那么选取下一段的准则就是能覆盖到b点,且右端点最大的区间。
这就相当于解决了选点的中间过程,接下来要解决的就是起点和终点问题
容易得到,第一个区间就是包含给定区间左端点且右端点最大的区间
而终点就是所选区间已经包含给定区间的右端点,一旦达到,即刻终止。

总结:

针对这种怎样取得最值的问题,要保持用贪心算法的求解的警惕性。而贪心算法最重要的就是贪心准则,在实验中发现起点和终止条件也很重要。

在实验中获得的另一个教训是是想问题要全面,注意一旦明确可以或者不可以的情况,要及时终止,这里讲一点细节问题。比如假设找到的第一个区间就已经可以覆盖所有区间的情况就可以实现,能终止。要考虑中间过程中的情况,当下一个区间的左端点都比寻找的区间右端点大的时候就肯定无法实现,可以终止。同时要对执行完的情况给出反馈。

再一个教训就是需要对输入的数据进行排序,再进行运算,这样能保证顺序处理就是结果,如果数据是乱的,结果就会出现很大的问题。

代码:

#include<stdio.h>
#include<iostream> 
#include<algorithm>
using namespace std;
struct section{
	int x;
	int y;
	bool operator < (const section &e)const{   //先按照左端点,然后按照右端点排序 
		if(x!=e.x)	return x<e.x;
 		if(y!=e.y)	return y<e.y;
	}
};
section s[30000] ;
int fun(section *s,int n,int T)
{
	int	count=0;
	bool flag=false;
	
	section next,now;
	next.x=-1;next.y=-1;  //选出第一个起点 
	now.x=-1;now.y=-1;
	
	int i=0;
	for(;i<n;){
		while(s[i].y>=1&&s[i].x<=1)  //包含1的右端点最大的第一个区间 
		{
			if(s[i].y>now.y)
			now=s[i];
			i++;
			flag=true;
		}
		if(s[i].x>1)	break; 
		i++;
		//break;
	}
	if(!flag)	return -1; 
	flag=false;
	 
	count++;    //第一个元素 
	if(now.y>=T)	return count;
	for(;i<n;)
	{ 
		while(s[i].x<=now.y+1)   //选择右端点最大的 
		{
			if(s[i].y>next.y){
				next.y=s[i].y;
				//j=i;
			}
			if(next.y>=T) return count+1;
			flag=true;
			i++;
		}
		now=next;
		count++;
		if(!flag)	return -1; 
		flag=false;
	}
	if(now.y < T)	return -1;
	return count;
}
int main()
{
	int n,T;
	while(scanf("%d%d",&n,&T)!=EOF){
		for(int i=0;i<n;i++) {
			scanf("%d%d",&s[i].x,&s[i].y);
		}
		sort(s,s+n);
		int count=fun(s,n,T);
		printf("%d\n",count);
	}
}
发布了10 篇原创文章 · 获赞 0 · 访问量 171

猜你喜欢

转载自blog.csdn.net/weixin_45736432/article/details/104829661