抽签
试题描述
你的朋友提议玩一个游戏:将写有数字的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;
}