查找和是某一值或者某一范围的的所有可能

本文介绍了一道笔试题的解决方案,该题要求计算特定ASCII码范围内使用指定字母的所有可能组合数量。提供了两种算法实现,一种是通过迭代方法进行搜索,另一种是通过子集树来遍历所有可能性。

昨天做笔试题,有这么一道题,稍有难度。

题目是:

输入包含多组数据。第一行为一个正整数T(T<=1000),表示测试数据的组数。然后是T组测试数据,每组测试数据占两行,第一行包/含两个整数n和m,表示ASCII码的范围。第二行为所使用的大写字
母的掩码,共26个数字构成的串,对应于26个大写字母,表示使用/对应字母的数量。输出对于每一组输入数据,输出一行"Case #k ans",
表示第k组数据的结果是ans。
样例输入
2
1 100
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
100 200
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
样例输出
Case #1: 3
Case #2: 4
Hint
说明:请注意严格按照格式进行输出,如 Case #1: 3(Case的C大写,冒号后有空格)第一组数据即A,B,C各一个的情况下,求ASCII和在1到100的组合,共有三组:即A(65),B(66),C(67);第二组在同等情况下求和在100到200范围内的组合,共四组,即AB(131),AC(132),BC
(133),ABC(198),其余交换字母位置的可能不考虑。


首先看一下第一种方法。用两个变量收缩走,但是这样无法讨论不连续的情况。

//没有考虑到不连续的情况
#define A 65
int Fun(vector<int> &ve,string &str,int i)
{
	if(ve.size() < 1 || i < 0 || str.length() <= 0)
		exit(1);
	int small = ve[i*2];
	int big = ve[i*2+1];

	int start = 0;
	int end = 0;
	int max_cur = 0;
	int max_i = 0;
	while(end < str.length())
	{
		if(str[end] == '0')
		{
			end++;
			continue;
		}
		max_cur += (end +++ A);
		if(max_cur <= big && max_cur >= small)
		{
			max_i ++;
		}
		else if(max_cur < small)
			continue;
		else
		{
			max_cur -= (start +++ A);
		}
	}
	max_cur -= (start +++ A);
	while(max_cur <= big && max_cur >= small)
	{
		max_cur -= (start +++ A);
		max_i ++;
	}
	return max_i;
}
int main()
{
	int n;
	vector<int> ve;
	cin>>n;
	string * str = new string[n];
	for(int i = 0; i < n; ++i)
	{
		int tmp;
		cin>>tmp;
		ve.push_back(tmp);
		cin>>tmp;
		ve.push_back(tmp);
		cin>>str[i];
	}
	for(int i = 0; i < n; ++i)
	{
		int kn = Fun(ve,str[i],i);
		cout<<"Case #"<<i+1<<": "<<kn<<endl;
	}
	return 0;
}
这种情况有明显的缺点,但是我们可以有新情况,第二种情况是利用子集树来进行

代码如下:

#define A 65
void Fun(vector<int> &ve_tmp,int i,int len,int small,int big,int &num)
{
	int sum = 0;
	int k = 0;
	while(i)
	{
		if(i & 1)
		{
			sum += ve_tmp[k];
		}
		k++;
		i >>= 1;
	}
	if(sum >= small && sum <= big)
		num++;
}
int Fun(vector<int> &ve,string &st,int i)
{
	int small = ve[i * 2];
	int big = ve[i * 2 + 1];
	int num = 0;
	vector<int> ve_tmp;
	for(int i = 0; i < st.length(); ++i)
	{
		if(st[i] == '1')
		{
			ve_tmp.push_back(i + A);
		}
	}
	int len = ve_tmp.size();  
	for(int i = 0; i < (1 << len); ++i)
	{
		Fun(ve_tmp,i,len,small,big,num);
	}
	return num;
}
int main()
{
	int n;
	vector<int> ve;
	cin>>n;
	string * str = new string[n];
	for(int i = 0; i < n; ++i)
	{
		int tmp;
		cin>>tmp;
		ve.push_back(tmp);
		cin>>tmp;
		ve.push_back(tmp);
		cin>>str[i];
	}
	for(int i = 0; i < n; ++i)
	{
		int kn = Fun(ve,str[i],i);
		cout<<"Case #"<<i+1<<": "<<kn<<endl;
	}
	return 0;
}



7-16 二分搜索 分数 5 作者 cjyoleon 单位 哈尔滨理工大学 对于一个有序序列,我们可以用二分搜索的方式在该序列中搜索某个数值。 以升序序列为例,二分搜索首先确定搜索的范围(即搜索序列的第一个元素的位置 left 最后一个元素的位置 right),然后计算出中间位置 mid。比如我们要搜索的序列是:2, 7, 11, 19, 23, 31, 33, 39。在初始的状态下,搜索范围是整个序列,那么第一个元素的位置left就是1,最后一个元素的位置right就是8,mid = (left + right)/2 = 4(注意序列的长度是偶数,left+right的是奇数,这里对结果进行了向下取整)。 接下来,检查中间位置的元素是否是要搜索的 target(假设我们要搜索的是31,那么target就等于31),如果不是,则比较中间元素与 target 的大小关系:如果中间位置的元素比target大,因为序列是升序的,所以 target 只可能在中间元素的左侧,也就是位于 left mid - 1之间,此时把 right 更新为 mid - 1,也就是说,下次我们要搜索的范围的右边界发生了变化。更新right后,再次计算新的mid,重复上面的搜索过程;如果中间位置的元素比 target 小,那么 target 只可能位于 mid + 1 r 之间,此时把 l 更新为 mid + 1(这种情况是左边界发生了变化)后,重复上面的搜索过程。 在搜索过程中,旦发现某次,搜索范围中间的等于target,那么就是找到了目标或者始终找不到的情况下,当left > right的时候,我们就知道target不在我们搜索的序列中,此时搜索也就结束了。 以在序列(2, 7, 11, 19, 23, 31, 33, 39)中搜索31为例,说明下二分搜索的流程。 第轮搜索:left = 1,right = 8(注意在大多数编程语言中,数组下标是从0开始的),mid = (1 + 8) / 2 = 4,序列中第四个元素是19,19小于要搜索的目标31,因此31只可能在序列的后半段,此时更新左侧边界left,把left赋为mid + 1,此时left变成了5; 第二轮搜索:left = 5,right = 8,mid = (5 + 8) / 2 = 6,序列中第6个元素是31,此时找到了目标元素,其在序列中的位置是6,搜索结束。 再以要搜索的是30为例,我们从刚才的第二轮搜索继续。到了上面的第二轮搜索,序列中第6个元素是31,大于30,那么30只可能存在于第5个元素(left的为5)的右侧、31(第6个元素)的左侧,此时更新right,将其赋为mid - 1,此时right变成了5。继续进行第三轮搜索。 第三轮搜索:left = 5,right = 5,mid = (5 + 5) / 2 = 5。序列中第5个元素是23,小于30,此时更新left为mid + 1,left的变为6,继续进行第四轮搜索。 第四轮搜索:在进行搜索前,发现left的已经大于right,这时可以确定要搜索的不在序列中,搜索结束。 输入格式: 输入包括三行。 第行为一个[1, 100]区间内的整数n,表示升序序列的长度; 第二行为n个[1, 1000]区间内的整数,这些整数以升序排列,各整数之间以空格分隔; 第三行为一个[1, 1000]区间内的整数,表示要在上述升序序列中搜索的V。 输出格式: 若在升序序列中搜索到了V,则输出V的位置(序列中的元素从1开始编号); 若未找到V(升序序列中不存在V),则输出Value not found. 输入样例1: 10 2 7 11 19 23 31 33 39 41 45 31 输出样例1: 6 输入样例2: 10 2 7 11 19 23 31 33 39 41 45 21 输出样例2: Value not found. 代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB C++ (g++) 1
最新发布
11-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值