week3 ——深搜&贪心算法

Problem 1 选数问题

问题描述

在n个数中,选k个数使得和恰好为sum,有多少种组成的结果(原题懒得翻译过多,总的来说就是这么个意思)

输入

第一行:T表示会有多少个测试样例
第二行:n,K,S表示从n个数中,选K个数,使得结果为S
第三行:n个数

输出

输出可组成的方案数

问题分析

1.n个数中选k个,这道题很明显就涉及到了搜索的知识。这里不需要求最佳解,于是宽搜深搜都可以,只是需要做好优化。
2.我这里采用的是深搜,首先先对存放那n个数的数组按升序的顺序进行一个排序。然后就调用函数开始执行深搜。由于是有序的,所以在加法进行到第q个数的时候(q<k),只要这前q个数选上的数的和已经大于我们要求的sum,那么这个状态就是不可取的,这条路就可以被pass掉。
3.在我的函数里面,addition(int layer,int sum,int i)分别代表了选了几个数,离目标状态还差多少,以及现在是对第几个数进行操作。

代码

#include<iostream>
using namespace std;
#include<algorithm> 
int shu[20];
int k,s,n;
int step;
bool cmp(int a,int b)
{
	return a<b;
}
void `addition(int layer,int sum,int i)`//c层,剩下的数,第i个数的选择与否 
{
	if(layer==k)
	{
		if(sum==0)step++;
		//cout<<"加一"<<endl;
		return;
	}
	else
	{
		if(sum<0) return;
		if(i>=n)return;
		addition(layer+1,sum-shu[i],i+1);
		addition(layer,sum,i+1);
		//cout<<"过程1"<<endl;
	}
}
int main()
{
	int T;
	
	cin>>T;
	for(int i=0;i<T;i++)
	{
		cin>>n>>k>>s;//k个数,和为s 
		step=0;
		for(int j=0;j<n;j++)
		{
			cin>>shu[j];
		}
		//cout<<"进入深搜前"<<endl;
		sort(shu,shu+n,cmp);
		addition(0,s,0);
		//cout<<"搜完了"<<endl;
		cout<<step<<endl;
	}
 } 

遇到的问题

对于宽搜需要准备传参的数据没有提前规划好,只是想到啥就加上啥。导致后来还找了半天的错才发现是最开始引用addition函数时用的传进去的数据不对,导致调用的函数直接出去。

problem2——区间选点

问题描述

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

输入

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

输出

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

问题分析

1.总体上先来分析一波这个题的解体思路,换一种说法就是选最少的点使得区间全部都有至少一个的点。虽然区间是有限制的,但是一开始你对于要选多少个点是没有准确的值的。所以说遍历肯定不可取,花费的功夫太大了需要2^100次,所以我们必须得采用个节约运算次数的算法——贪心算法。
2. 首先,设立结构体section,分别储存一段区间的左右断点。
然后对sections按照右端点的大小来排序,当右端点一样时,再按照左端点的大小来排序。if(x.b!=y.b) return x.b<y.b;else return x.a>y.a;
3.然后从sections[0]开始,选其右端点为选中的点,按着顺序往后走,一旦有区间无法被这个点占用,就换成该区间的右端点。以此类推。
为何每个区间都能恰好包含上?
原因:假设现在选到了第m个区间上的点,此时上一个选中的顶点R是m区间的右端点。因为我们排序的大小首先是按照区间的右端点来排序的,所以m+1区间是不可能在R之前就结束了,即m+1区间的右端点一定在R的右边,于是m+1区间只会有包含R点和全部在R点右边两种情况,如果是第二种,两区间完全没有交集肯定得+1。第一种的话也很明显的可以采用同一个点,不需要再多加点。

代码

#include<iostream>
using namespace std;
#include<algorithm> 
#include<vector>
struct section
{
	int a;
	int b;
};
bool cmp(section x,section y)
{
	if(x.b!=y.b)
		return x.b<y.b;
	else
		return x.a>y.a;
}

section sections[100];
int shu;
int minnum;
int N;
vector<int> q;
void setpoint()
{
	int point=sections[0].b;
	
	q.push_back(point);
	
	for(int j=1;j<N;j++)
	{
		if(point<sections[j].a)
		{
			point=sections[j].b;
			q.push_back(point);
		}
	}

	
}
int main()
{
	
	int a,b;
	shu=0;
	minnum=10000;
	cin>>N;
	for(int i=0;i<N;i++)
	{
		cin>>sections[i].a>>sections[i].b;	
	}
	sort(sections,sections+N,cmp);
	
	setpoint();
	cout<<q.size()<<endl;
	//cout<<"选的数  "<<endl;
	//vector<int>::iterator it;
	//for(it=q.begin();it!=q.end();it++)
    //cout<<*it<<endl;
	return 0;
}

遇到的问题

最开始的排序不对,我一开始采用的是先以左端点排序,然后这个想法对待部分数据(比如样例)是一样的答案。然后我就找了半天的错误,才找到这个地方这么做要对一些。个人认为左端点排序更适合下面那道题一些,就是对于区间的范围很在意的题目,会好一些。

problem3——区间覆盖

问题描述

数轴上有 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.同上题一样的结构体section,记录区间左右端点。对sections进行排序,先按照左端点的大小来排序,再按照右端点进行排序。(a.left<b.left||(a.left==b.left&&a.right>b.right));
3.在挨个搜索时,先注意考虑特殊情况,比如第一个区间就能够覆盖整个线段。再比如,根本就没有左端点小于等于1的区间,又或者根本就没有比t大的右端点。这些情况直接对应结果即可。
4.我先做了一个初步的减小范围,就是先遍历一遍,把左端点大于t的区间排除出去。
5.然后,我用current来保存当前已经被覆盖的线段点。先把第一个区间加进去。当上一个加入的区间为m时,从m+1开始遍历,找到所有序号比m大,且左端点在current所保存的区间里。随后找到那个加入能使得current的右端点变化最大的区间。把它加进来,并对current更新。重复操作直到所有在范围内的点都被找完,或者到达t了。

代码

#include<iostream>
using namespace std;
#include<algorithm> 
int N,T;
struct section
{
	int left;
	int right;
};

bool cmp(section a,section b)
{
	return (a.left<b.left||(a.left==b.left&&a.right>b.right));
}
section sections[25000];
int main()
{
	//cin>>N>>T;
	scanf("%d%d",&N,&T);
	for(int i=0;i<N;i++)
	{
		//cin>>sections[i].left>>sections[i].right;
		scanf("%d%d",&sections[i].left,&sections[i].right);
	} 
	sort(sections,sections+N,cmp);
	int shu=N; 
	int step=1;
	int maxm=1;
	for(int i=0;i<N;i++) {
        // 和目标线段有交集的点数,剪枝1 
        if(sections[i].left>T+1 )
        {
        	shu=i;
        	break;
		}
		else
		{
			if(sections[i].right>maxm)
				maxm=sections[i].right;
		}
    }
    section current;//当前覆盖的左右交点; 
    if(sections[0].left!=1||maxm<T)
    {
    	cout<<-1<<endl;
    	//cout<<sections[0].left<<endl;
    	//cout<<maxm<<endl;
    	return 0;
	}
	else
	{
		current.left=1;
		current.right=sections[0].right;
		if(current.right>=T)//第一个就满足条件 
		{
			cout<<1<<endl;
			return 0;
		}
		 //已经弄到哪一个数了
		int rright=0;//找到的最右边的点
		int minus;//差值 
		int pan=0;
		while(true)
		{
			minus=0;
			int i;
			for( i=rright;i<shu&&sections[i].left<=current.right+1;i++)//遍历所有与其有交点的 
			{
				if(sections[i].right-current.right>minus)
				{
					minus=sections[i].right-current.right;
					rright=i;
				}
				//cout<<i<<"    "<<minus<<"    current  "<<current.right<<endl; 
			}
			//cout<<i<<"    "<<minus<<"    current  "<<current.right<<endl; 
			if(minus==0)//剩下的交点中没有可让current区间更新的小区间 
			{
				break;
			}	
			else
			{
				step++;
				current.right=sections[rright].right;
				//cout<<"  下一个是 "<<sections[rright].left<<"   "<<sections[rright].right<<endl;
				//cout<<"当前覆盖区间 "<<current.left<<"   "<<current.right<<endl;
				if(current.right>=T)
					break;
			}
		}
		if(current.right>=T)
			cout<<step<<endl;
		else 
		{
			cout<<-1<<endl;
			//cout<<" minus=" <<minus<<"    current="<<current.right<<endl; 
		}
			
	}
    
}

遇到的问题

一开始不太清楚思路,在网上找到了对于思路的文字描述,自己写起来却发现自己老是会漏掉一些情况没有考虑。像比如我的rright的初始化忘记做了。没有考虑到第一个区间就能够把线段覆盖完的情况。都是对于代码不熟悉的结果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值