选数,区间选点(覆盖)

**

选数问题

输入T,将有T轮选数。给出三个数n,K,S,表示接下来一行有n个数输入,要求从中选出K个数的和等于S。输出有几种方法

分析

本题采用DFS。

思考第一个问题——如何转化为我们熟悉的DFS问题。
将步数K和总和S转化为坐标。DFS开始时,(第一步,和为零)=(1,0),结束时(第K+1步,和为S)=(k+1,S),即从(1,0)到(K+1,S)。注意定义中的第一步和第K+1步表示将要进行第一步或第K+1步,并不是这一步已经完成。另一种理解也可以定义为从(0,0)到(K,S)。

再思考另一个问题——如何选数。
假设,有一个数组 t [1-n]。我们第一步已经选了 t [1],那么下一轮递归一定要在 t [2-n]。总结一个规律,本次递归选择 t [i],下一轮只能在 t [(i+1)-n]选数。
由此我们知道,DFS需要三个参数——it选数起始位置,step当前进行步数,sum已经累加的和。

最后一个问题——如何递归和设定边界
尝试选择一个数,直接递归。那么需要一个循环,如果当前剩余能选数个数<剩余步数(T-it<K-step),将无法选够K个数循环结束。
边界有两个,当到达(K+1,S),即step=K+1,sum=S,满足条件。
另一个边界,步数超过能选个数,和超过S(sum等于S但step未到达K也包含在内),不足选够K个数,这个边界不满足条件且该路径永远不可能满足条件。

代码

void DFS(int it,int step,int sum){
	if(sum==S&&step==K+1){   //判定成功 
			X++;
			return;
	}
	if(step>K||sum>=S||T-it<K-step){//本条路径已经不能到达终点 
		return;
	} 
	int s;
	for(;T-it>=K-step;it++){
		s=sum;
		s+=t[it];
		DFS(it+1,step+1,s);
	}
}

第一次调用DFS和输出

		DFS(1,1,0);
		cout<<X<<endl;

区间选点(覆盖)

区间选点:数轴上有 n 个闭区间 [a_i, b_i]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。
输入输出:第一行1个整数N(N<=100),第2~N+1行,每行两个整数a,b(a,b<=100)。输出一个整数,代表选点的数目。
区间覆盖:数轴上有 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。

分析

采用贪婪算法

选点问题。

首先,对区间排序,每个区间起始点按字典顺序,终点也按字典顺序。
每次选点,尽量选择区间的终点。如果当前区间已经包含选点集任一个,跳过判定下一个区间。如果当前区间没有被选中,选择终点加入选点集。
记录每次选点的最后一个end,这样不用每次判定都去遍历选点集。

代码

#include<iostream>
#include<algorithm>

using namespace std;

struct s{
	int a,b;
}v[100];
bool cmp(s x,s y){
	return x.b==y.b?x.a>y.a:x.b<y.b;
}

int main(){
	int n,m=0;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>v[i].a>>v[i].b;
	}
	sort(v,v+n,cmp);
	int end=-1;
	for(int i=0;i<n;i++){
		if(end<v[i].a){
			m++;
			end=v[i].b;
		}
	}
	cout<<m;
}

区间覆盖问题。

仍然需要先排序,起始点的字典序,终点的字典序。
需要覆盖一个区间【1,n】,那么我们每次选择的区间最右点必须距离点n最近。

问题1:如何找出可选区间。
记录一个点,end已选区间的最右点,初始0。判定区间,最左点是否能够衔接end,即左点<=end+1。最右点是否大于end,如果小于等于该区间没有必要选的(要找出最少的区间),右点>end。

问题2:如何选出最优区间(可能两个区间都可选,但其中一个距离点n最近)
记录一个点,newend待选区间的最右点(没有确认选,正在筛选中的最右点),初始为0。
当选择区间时,不会之间加入区间集中,先记录newend,继续对后序区间判定,找出最优区间。

问题3:如何确定一个区间,如何提前知道该路径已经不能覆盖整个区间
当选出的区间不能衔接end点,但是能够衔接newend点,那么newend点代表的区间可加入区间集。如果同时也不能衔接newend点,说明即便选定newend代表的区间,下一个点也不能衔接上,将不能完全覆盖【1,n】

代码

	for(int i=0;i<n;i++){
		scanf("%d%d",&v[i].a,&v[i].b);
	}
	sort(v,v+n,cmp);
	int end=0,newend=0;
	for(int i=0;i<n&&newend<t;i++){
		if(v[i].a<=end+1&&v[i].b>end){
			if(v[i].b>newend){
				newend=v[i].b;
			}
		}
		else{
			if(v[i].a<=newend+1&&v[i].b>newend){
				end=newend;
				newend=v[i].b;
				m++;
			}
			else{
				if(v[i].a>newend+1){
					m=-2;
					break;
				}
			}
		}
	}
	m++;

要注意当我们已经覆盖这个区间后,newend代表的点并没有并入区间,m加一。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值