每日一题 抽签(优化)

抽签

试题描述

你的朋友提议玩一个游戏:将写有数字的n 个纸片放入口袋中,你可以从口袋中抽取4 次纸片,每次记下纸片上的数字后都将其放回口袋中。如果这4 个数字的和是m,就是你赢,否则就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当纸片上所写的数字是k1,k2, …, kn 时,是否存在抽取4 次和为m 的方案。

输入
第一行为两个整数n,m;第二行为n个整数k1,k2, …, kn 。

输出
如果存在,输出“Yes”;否则,输出“No”。

输入示例1

3 10
1 3 5

输出示例1

Yes

输入示例2

3 9
1 3 5

输出示例2

NO

其他说明
1 ≤ n ≤ 50
1 ≤ m ≤ 108
1 ≤ ki ≤ 108

一.无脑暴搜(数据范围比较小) 时间复杂度: O(n4

#include<iostream>
#include<algorithm>

using namespace std;
const int N = 60;
int a[N];
int n, m;
int main()
{
	cin >> n >> m;
	for (int i = 0;i < n;i++) cin >> a[i];
	
	bool res = 0;
	for (int i = 0;i < n;i++)
		for (int j = 0;j < n;j++)
			for (int k = 0;k < n;k++)
				for (int q = 0;q < n;q++)
					if (a[i] + a[j] + a[k] + a[q] == m) res = 1;

	if (res) cout << "Yes" << endl;
	else cout << "No" << endl;
	return 0;
}

二.第一次优化 时间复杂度:O(n3 logn)
暴搜时我们找的是:k1+k2+k3+k4=m
在此我们可以用二分法将式子优化为寻找:k4=m-k1-k2-k3
在三重循环的基础上将剩余数字从小到大排序,利用二分法 将 x=m-k1-k2-k3 与中位数作比较,如果x小,说明k4在数组的后半段,反之说明k4在数组的前半段。

这样时间复杂度:
排序:O(nlogn)
搜索:O(n3logn)
n3logn 远大于 nlogn所以这里时间复杂度就是 O(n3logn)

#include<iostream>
#include<algorithm>

using namespace std;
const int N = 60;
int a[N];
int n, m;
bool check(int x)
{
	int l = 0, r = n-1;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (a[mid] == x) return true; //找到

		if (x < a[mid]) r = mid;
		else l = mid + 1;
	}
	return false;
}
int main()
{
	cin >> n >> m;
	for (int i = 0;i < n;i++) cin >> a[i];
	sort(a, a + n);
	bool res = 0;
	for (int i = 0;i < n;i++)
		for (int j = 0;j < n;j++)
			for (int k = 0;k < n;k++)
			{
				if(check(m - a[i] - a[j] - a[k])) res=1;  //利用二分法判断k4在不在数组中
			}
					

	if (res) cout << "Yes" << endl;
	else cout << "No" << endl;
	return 0;
}

三.第二次优化 时间复杂度 O(n2logn)
第一次优化后时间复杂度O(n3logn)还是有点大。
为此仿照第第一次优化的方法寻找 k3+k4 = m-k1-k2
所以先将数组中两两求和,再排序,剩下同第一次优化。

排序:n2logn (这个我也不是很懂)
搜索:n2logn
总的也是O(n2logn)。

#include<iostream>
#include<algorithm>

using namespace std;
const int N = 60;
int a[N],b[N*N];
int n, m;
bool check(int x)
{
	int l = 0, r = n-1;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (b[mid] == x) return true; //找到

		if (x < b[mid]) r = mid;
		else l = mid + 1;
	}
	return false;
}
int main()
{
	cin >> n >> m;
	for (int i = 0;i < n;i++) cin >> a[i];

	int k = 0;
	for (int i = 0;i < n;i++)
		for (int j = 0;j < n;j++)
			b[k++] = a[i] + a[j];

	sort(b, b + N*N);

	bool res = 0;
	for (int i = 0;i < n;i++)
		for (int j = 0;j < n;j++)			
			if(check(m - a[i] - a[j])) res=1;  //利用二分法判断k3+k4在不在数组中
			
	if (res) cout << "Yes" << endl;
	else cout << "No" << endl;
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值