算法迭代优化之路,找出NP完全问题,避免出现NP完全问题。

                     **有这么一个“抽签”问题**

你的朋友提议一起玩个游戏,将写有数字的N个纸片放入口袋中,你可以从口袋中抽取四次纸片,每次记下纸片上数字后都将其再放回口袋。如果这4个数字的和是M,就算你赢了,否则就是你的朋友赢了。你挑战了好几次,结果一次都没有赢过,于是怒而撕破口袋,取出所有纸片,检查自己是否真的有赢的可能。请编写一个程序解决这个问题,如果存在则输出Yes,否则输出No.
限制条件
1 <= n <= 50
1 <= m <= 10**8
1 <= k <= 10**8
输入
n  = 3
m = 10
k = {1,3,5}
输出
yes因为例如抽取四次1、1、3、5和就是10
输入
n = 3
m = 9
k = {1,3,5}

输出
no(不存在和为9的抽取方案)

n = input()
m = input()
k = input()
def solve():
    flag = False
    for a in range(n):
        for b in range(n):
            for c in range(n):
                for d in range(n):
                    if k[a] + k[b] + k[c] + k[d] == m:
                        flag = True
    if flag:
        print("Yes")
    else:
        print("No")

solve()

我们考虑到上次如果n这个值无限增大,那么就很容易出现NP完全问题了。为了避免NP=P?出现,我们必须要优化自己的算法。
思路我们可以用二分搜索减少检索次数。
要记住所要查找的值m-ka-kb-kc为x,预先吧数组排序好,然后看K中间的数就可以了。可知
如果它比x小,x只能在它的后半段
如果它比x大,x只能在它的前半段

排序O(nlogn)时间
循环O(n3logn)时间
n
3logn比logn大,我们这里合起来算O(n3longn)时间,于是,我们得到了抽签问题的在O(n3logn)时间内解决问题的算法。

n = input()
m = input()
k = input()
def binarySearch(x):
    left = 0
    right = len(k)-1
    while right - left >= 1:
        mid = (left + right) // 2
        if k[mid] == x:
            return True
        elif k[left] > x:
            left = mid + 1
        else:
            right = mid
    return False
def solve():
    sorted(k)
    flag = False
    for a in range(n):
        for b in range(n):
            for c in range(n):
                if binarySearch(m - k[a] - k[b] - k[c]):
                    flag = True
    if flag:
        print("Yes ")
    else:
        print("No")
        
solve()
    

那么我们可以优化到O(n**3logn)是不是这个工程问题的最优解呢,答案不是,我们仍然有更快速的解决方案。
假设我们有四个数,我们可以分成两两考虑的想象力,这也叫分而治之,在大禹治水时期,古人就能想到这个高明的方法来治水,我们今天可以拿来优化我们的算法。此外,像这样从复杂度较高的算法出发,不断降低复杂度知道满足问题要求的过程,也是设计算法必须经历的一个过程。
我们用分而治之这种思想来优化我们的算法
检查是否有c和d使得kc + kd = m-ka-kb

这种情况并不能直接使用二分搜索。但是,如果预先枚举出kc+kd所得n2个数字并排好序,便可利用二分搜索了。
该算法
排序O(n
2logn)时间
循环O(n2logn)时间
总的也是O(n
2logn)时间,这样可以确信即便n=1000000也能妥善应对了。

n = input()
m = input()
k = input()
def binarySearch(x):
    left = 0
    right = len(k)-1
    while right - left >= 1:
        mid = (left + right) // 2
        if k[mid] == x:
            return True
        elif k[left] > x:
            left = mid + 1
        else:
            right = mid
    return False
# 创建一个新列表,用来存放c和d的和。
kk = [0]*n*n
def solve():
    flag = False
    for c in range(n):
        for d in range(n):
            kk[c*n+d] = k[c] + k[d]
    sorted(kk)
    for a in range(n):
        for b in range(n):
            if binarySearch(m - k[a] - k[b]):
                flag = True
    if flag:
        print("Yes")
    else:
        print("No")
    
solve()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值