抽签问题(算法例题讲解)

抽签问题

时间: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;
}

本次刷题心得:
温习了一遍二分搜索,还学会了两个优化小技巧,通过把判断等式移项来获得一个新的判断条件,另一个是,可以通过构建一个数组来储存两次循环的结果,以此来代替循环真的是妙啊。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值