抽签问题
时间:4.28
本次学习记录来源于《挑战程序设计竞赛(第2版)》
抽签问题
题目描述:
你的朋友提议玩一个游戏:将写有数字的n个纸片放入口袋中,你可以从口袋中抽取4次纸片,每次记下纸片上的数字后都将其放回口袋中。如果这4个数字的和是m,就是你赢,否则就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当纸片上所写的数字是k,k2,…, k,时,是否存在抽取4次和为m的方案。如果存在,输出 Yes;否则,输出 No。
限制条件:
1 ≤ n ≤ 50
1 ≤ m ≤ 10^8
1≤ ki ≤10^8
题目分析:
这题的常规想法肯定是用四个for循环来写,但是很显然这样行不通,指数时间在数据大的时候定会超时。这个时候就要优化一下算法。
首先想到的一点是改进搜索方法,把常规的顺序搜索改为二分搜索这样就会大大提升效率。
其次是优化内层循环呀,这个四层循环简直是累死人啊,所以最内层的循环可以采用一种巧妙的方法来替换掉。
还可以继续改进,把最后两层循环都改进一下,构建一个数据枚举前两次循环的两数之和,用以作为最后一层循环的判断条件。以达到最优的效果。
参考代码:
//未优化前,时间复杂度O(n^4)
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int a[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
bool flag = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
for (int l = 0; l < n; l++) {
if (a[i] + a[j] + a[k] + a[l] == m) {
flag = true;
}
}
}
}
}
if (flag) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
return 0;
}
//优化一次后,时间复杂度O(n^3logn)
#include<bits/stdc++.h>
using namespace std;
bool binary_search(int a[], int n, int x) {
int l = 0, r = n - 1;
sort(a, a + n);
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] == x) {
return true;
}
else if (a[mid] < x) {
l = mid + 1;
}
else {
r = mid - 1;
}
}
return false;
}
int main() {
int n, m;
cin >> n >> m;
int a[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
bool flag = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
//这个条件就是通过判断等式移项而来,这样就省去了一层循环
if (binary_search(a, n, m - a[i] - a[j] - a[k])) {
flag = true;
}
}
}
}
if (flag) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
return 0;
}
//第三次优化循环,使时间复杂度缩减到了O(n^2logn)
#include<bits/stdc++.h>
using namespace std;
bool binary_search(int aa[], int n, int x) {
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (aa[mid] == x) {
return true;
}
else if (aa[mid] < x) {
l = mid + 1;
}
else {
r = mid - 1;
}
}
return false;
}
int main() {
int n, m;
cin >> n >> m;
int a[n];
int aa[n * n]; //保存两个数的和的数列
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
aa[i * n + j] = a[i] + a[j];
}
}
sort(aa, aa + n * n);
bool flag = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (binary_search(aa, n * n, m - a[i] - a[j])) {
flag = true;
}
}
}
if (flag) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
return 0;
}
本次刷题心得:
温习了一遍二分搜索,还学会了两个优化小技巧,通过把判断等式移项来获得一个新的判断条件,另一个是,可以通过构建一个数组来储存两次循环的结果,以此来代替循环真的是妙啊。