由抽签问题谈谈时间复杂度

你的朋友提议玩一个游戏:将写有数字的 n个纸片放入口袋中,你可以从口袋中抽取 4次纸
片,每次记下纸片上的数字后都将其放回口袋中。如果这 4个数字的和是 m,就是你赢,否
则就是你的朋友赢。你挑战了好几回,结果一次也没赢过,于是怒而撕破口袋,取出所有纸
片,检查自己是否真的有赢的可能性。请你编写一个程序,判断当纸片上所写的数字是 k 1 ,
k 2 , …, k n 时,是否存在抽取 4次和为 m的方案。如果存在,输出 Yes;否则,输出 No。
限制条件
 1 ≤ n ≤ 50
 1 ≤ m ≤ 10 8
 1 ≤ k i ≤ 10 8
输入
n = 3
m = 10
k = {1, 3, 5}
输出
Yes(例如4次抽取的结果是1、1、3、5,和就是10)

求解这个问题,可由题面写出程序

#include<iostream>
using namespace std;
const int MAX_N = 50;

int main()
{
    int m,n,k[MAX_N];
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        cin>>k[i];
    }
    bool f=false;
    for(int a=0;a<n;a++)
    {
        for(int b=0;b<n;b++)
        {
            for(int c=0;c<n;c++)
            {
                for(int d=0;d<n;d++)
                {
                    if(k[a]+k[b]+k[c]+k[d]==m)
                        f = true;
                }
            }
        }
    }
    if(f)
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
    return 0;
}

用四重循环即可解决,当然程序运行是有时间限制的,在大多数比赛中,运行时间限制在几秒,一旦程序运行时间超过了限制,程序就会被强行结束,当做错误的解答处理,因此,在做题中要尽量寻找和思考有效的解题方法。

如本体的四重循环,如果n<50,不用1秒就可以给出答案,但是如果n<1000呢?就肯定不会通过了,所以需要寻求更有效的方法。

改进思路:上面最初循环的部分,最内侧需要做的事就是检查是否有d使得
k[a]+k[b]+k[c]+k[d]=m
通过对式子移项,得到新的等式;
k[d]=m- k[a]+k[b]+k[c]
就是说,检查k中所有元素,判断是否有m- k[a]+k[b]+k[c],可以运用二分查找
来完成。
 排序O(nlogn)时间
循环O(n2logn)时间
所以该算法时间O(n3logn),对源程序改进得到:

int n,m,k[MAX_N];

bool binary_search(int x);
{
    int l=0,r=n;
    while(r-1>=1)
    {
        int i=(1+r/2);
        if(k[i]==x)
            return true;
        else if(k[i]<x)
            l=i+1;
        else
            r=i;
    }
    return false;
}

void solve()
{
    sort(k,k+n);
    
    bool f=false;
    
    for(int a=0;a<n;a++)
    {
        for(int b=0;b<n;b++)
        {
            for(int c=0;c<n;c++)
                if(binary_search(m-k[a]-k[b]-k[c]))
                  f=true;
        }
    }
    if(f)
        puts("Yes");
    else
        puts("No");
}

但是,将n=1000带入其中,发现这还是不够的,需要进一步优化,接下来,我们着眼于两个循环。和刚才同一思路,移项得
k[d]+k[c]=m- k[a]+k[b]
这种情况不能直接二分查找,需要先枚举k[c]+k[d]所得n*n种结果并排序,就可以利用二分搜索了。
 排序O(n2logn)时间
循环O(n2logn)时间
所以该算法时间O(n2logn),这样也能轻松通过

int n,m,k[MAX_N];

int kk[MAX_N];
bool binary_search(int x);
{
    int l=0,r=n*n;
    while(r-1>=1)
    {
        int i=(1+r/2);
        if(k[i]==x)
            return true;
        else if(k[i]<x)
            l=i+1;
        else
            r=i;
    }
    return false;
}

void solve()
{
    
    for(int c=0;c<n;c++)
    {
        for(int d=0;d<n;d++)
        {
            kk[c*n+d]=k[c]+k[d];
        }
    }
    sort(kk,kk+n*n);
    
    bool f=false;
    for(int a=0;a<n;a++)
    {
        for(int b=0;b<n;b++)
        {
                if(binary_search(m-k[a]-k[b]))
                  f=true;
        }
    if(f)
        puts("Yes");
    else
        puts("No");
}



本题需要掌握二分查找这一算法,也要有基本的设计算法的一些能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值