有两个序列a,b,大小都为n,序列元素的值时任意型整数,无序:要求交换a,b中的元素,是序列a元素的和与序列b元素的和之间的差最小。

问题阐述:

1.有两个序列a,b,大小都为n,序列元素的值时任意型整数,无序:要求交换a,b中的元素,是序列a元素的和与序列b元素的和之间的差最小。

问题分析:

1.经过很长时间的思考,并通过浏览各路大神的分析,本小白最终锁定此问题是背包问题。
2.将两个列表合成一个列表命名为c,所有元素求和得到sum,最终得到的两个列表为deal_a 和deal_b

搞成背包问题,看看有几个约束

1.差值最小,我们可以预测到,最终得到的两个数组一定在sum/2 的周围,
可以假设deal_a<=sum/2 和deal_b>=sum/2。
我们只需要找到deal_a, 那么从c中将deal_a的元素剔出来剩下的就是deal_b
2.由1的分析得出,只需要找出deal_a即可,有一个约束条件sum/2,即deal_a中的元素之和<=sum/2
3.从题目可以看出,还有一个约束条件,即deal_a的元素个数应该等于n
4.归纳成背包问题,有一个背包,可以装n块宝石,最大装sum/2千克,可以获得的最大价值是多少?(最大价值也是deal_a元素的和)
5.deal_a中所有元素的和既是你的目的,同时也限制着你的选择,所以可以从两方面考虑,并不冲突。

最终:

我们把该问题搞成了一个拥有二重约束的背包问题:具体二重约束的背包问题算法请阅读dd大神的《背包九讲
由于最后要把最终数组输出,需将其回溯出来,所以不考虑降维(当然也是由于菜鸡很菜,能力有限)

代码注意事项:

1.容量sum/2,sum是对c中的元素求和,可以推测,有负数的时候,sum/2小于c(共2n个)中任取n个较大值,容量可能会溢出,那就不行了,但是又不能随便加大容量,那样就起不到分割deal_a和deal_b的作用了,所以为了消除负数的影响,我们只需要将列表每个元素都增大即可。
2.deal_a中的值应该有n个,但是背包是找到最优的存放方法,某种情况下,例如n-1个数就可以达到sum/2,这显然与我们的要求不符,我们是要求deal_a中有n个值的,所以这里不仅仅在消除负数的影响的时候平移,一般时候也平移,平移多少呢,c中每个元素加上c中最大值即可,如果绝对值最大的是负数也没关系,我们平移的时候同时考虑这两者即可。(如此,一些基本的,偏僻的,只要是不是特别特别特殊的案例都可以测试通过)
3,2中的方法有些投机嫌疑,但菜鸡本菜,背包用得不好,希望大佬能多多指点。

#二重约束条件的背包问题
#测试数组,从各个博客中搞来的测试数组
#[5,5,9,10],[4,7,7,13]
#[-3,9,10,65],[5,6,13,55]
#[0,0,-3,5],[0,0,1,1]
#[100,99,98,1,2, 3],[1, 2, 3, 4,5,40]
#[1,5,5,7,14],[4,5,7,7,9]
###################################################################
import numpy as np
list_a = [5,5,9,10]
list_b = [4,7,7,13]
list_c=(list_a+list_b)#每个物体的容量和价值是一样的
N=len(list_c)
#先检查有没有负数,若有负数整体平移
negative=0 if min(list_c)>=0 else min(list_c)#先将负数求和,其实找到最小的负数即可,但本着避免“代码注意事项”中2的问题多加点更好
negative=-negative+100 if max(list_c)<100 else max(list_c)#设置平移值,负数之和取反在加上c中的最大值
list_c=[i+negative for i in list_c]#整体平移
M=sum(list_c)//2+1
Q=N//2+1
N=N+1
bag=np.zeros((Q,M,N),dtype=int )#N是第几个物品,M是当前容量,Q是物体个数N/2
#如果看不懂背包部分,可以自己写写此部分,毕竟每个人代码细节都不同
for i in range(1,N):
    for j in range(1,M):
        for k in range (1,Q):
            if(j>=list_c[i-1]):
                bag[k,j,i]=max(bag[k][j][i-1],bag[k-1][j-list_c[i-1]][i-1]+list_c[i-1])
            else:
                bag[k][j][i]=bag[k][j][i-1]
#结束后最值存储在bag[Q-1][M-1][N-1]中,后面是将背包里的物品回溯出来
deal,i,j,k=0,N-1,M-1,Q-1
while True:
    if(bag[k,j,i]!=bag[k,j,i-1]):
        list_a[deal]=list_c[i-1]
        deal+=1;

        j-=list_c[i-1];i-=1;k-=1
    else:
        i-=1
    if j==0 or i==0 or k==0:
        break
#懒得设置新的变量,有list_a,list_b存结果(反正也他俩也用不到了QAQ)
N=N-1
#将list_b搞出来,从list_c中剔除list_a,不能用差集,因为会删除重复元素,不知道有啥好方法了,用个循环
for i in range(N//2):
    for j in range(N):
        if(list_a[i]==list_c[j]):
            list_c[j]=0
            break
i=0
for j in range(N):
    if(list_c[j]!=0):
        list_b[i]=list_c[j]
        i+=1
#移回来
list_a=[i-negative for i in list_a]
list_b=[i-negative for i in list_b]
print(list_a,list_b)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值