python数组分成两个和相等的子集_算法--将数组分成和相等的多个子数组,求子数组的最大个数...

作者:陈太汉

一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值

比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;

{3,6}{2,4,3} m=2

{3,3}{2,4}{6} m=3 所以m的最大值为3

算法 原理的思想是将大问题转换成小问题。

就{3,2,4,3,6}的操作步骤:

第一步:想将数组递减排序得{6,4,3,3,2},求出数组中所有数的和m=18,第一个最大的数b=6, m/b=3余数为0,

当除数为1,余数为0时终止。当余数不为0时,转到第三步。当余数为0时将数组划分为{6},{4,3,3,2}两个。把{4,3,3,2}看成一个新的数组。

第二步:先用{4,3,3,2}中的最大数与b=6比较,即4

结果相等,则将这两个数从该数组中除去生成新的数组,转到第一步,现在的结果是{6},{4,2},{3,3},把{3,3}看成一个新的数组继续重复第二步。

第三步,将数组中最大的数与最小的数取出构成一个新数组Z,剩余的构成一个数组,然后,

判断m/Z中数字之和看是否余数为0,若为0,把b替换为Z中数字之和转第二步,若不为0,

继续从剩余的数字中取出最小值加入到Z中,再判断m/Z中数字之和看是否余数为0,直到为0,转第二步为止。

最后得到的结果是{6},{4,2},{3,3} 这时可以计算出m为3,也可以在程序中作记载。

在第二步工程过,若出现两个数相加大于上一次的b,则将程序转到第三步。

上面是别人的分析,我想很多人跟我一样看了相当晕,但看了我的代码你应该不至于晕。有时候用文字表达还真是繁琐,但是代码却简单明了。

大家都说算法和数据结构对程序员来说很重要,还说比的就是这个,我看未必,我觉得更重要的还是分析问题的能力,你要是能把问题分析得相当透彻,我相信你也能写出相应的代码。

很多问题看起来复杂,但是等你分析清楚了,还是相当简单的。这道算法面试题的代码是相当简单啊!

源代码

#includeusingnamespacestd ;classFindMaxM

{public:intFindM(intarr[],intlength)

{if(NULL==arr||length<=0)

{return-1;

}//倒序排序InsertSort(arr,length);intsum=0;//数组的和for(inti=0;i

{

sum+=arr[i];

}intend=length-1;intsubSum=arr[0];while(sum/subSum>=2)

{if(sum%subSum==0)

{returnsum/subSum;

}

subSum+=arr[end--];

}return-1;

}private://用数组实现插入排序inlinevoidInsertSort(intarr[],intlength)

{intcur;for(inti=1;i

{

cur=arr[i];for(intj=0;j

{if(arr[j]

{for(intk=i;k>j;k--)

{

arr[k]=arr[k-1];

}

arr[j]=cur;break;

}

}

}

}//用指针实现选择排序inlinevoidSelectSort(int*ptArr,intn)

{for(inti=0; i

{intk=i;for(intj=i+1; j

{if(*(ptArr+j)>*(ptArr+k))

{

k=j;

}

}if(k!=i)

{inttmp=*(ptArr+k);*(ptArr+k)=*(ptArr+i);*(ptArr+i)=tmp;

}

}

}

};

排序用了两种方实现。插入排序和选择排序就不多说了,大家都懂。

我要说的是用数组实现排序和用指针实现排序,个人认为指针排序速度要比数组排序快(没有测试),

指针直接访问元素的地址,而数组要先计算元素的偏移,才能得出元素的地址

这是一个经典的NP完全问题,称为“子集和问题”。因此,不存在一个有效的算法来解决该问题,除非P=NP。 但是,我们可以使用回溯算法来解决这个问题。具体来说,我们可以从第一个元素开始,将其添加到第一个子集中,并递归到下一个元素。如果添加当前元素后,第一个子集的和不超过目标和,则继续向下递归;否则,我们可以回溯并尝试将当前元素添加到第二个子集中。如果我们功地将所有元素分配到了各个子集中,并且每个子集的和都等于目标和,则我们找到了一个解。 以下是一个示例代码: ```python def can_partition(nums): s = sum(nums) if s % 2 != 0: return [] nums.sort(reverse=True) target = s // 2 res = [] subsets = [[] for _ in range(2)] backtrack(nums, 0, target, subsets, res) return res def backtrack(nums, index, target, subsets, res): if index == len(nums): if target == 0: res.append(subsets[:]) return for i in range(len(subsets)): if sum(subsets[i]) + nums[index] > target: continue subsets[i].append(nums[index]) backtrack(nums, index + 1, target, subsets, res) subsets[i].pop() ``` 在上面的代码中,我们首先计算数组的总和,并检查它是否是偶数。如果不是,我们可以直接返回空列表。否则,我们将数组排序,以便更快地找到解。我们还定义了一个目标和,即数组总和的一半。 然后,我们使用回溯算法来找到所有可能的子集分配方式。我们维护两个子集,并尝试将当前元素添加到第一个子集中。如果添加后,第一个子集的和不超过目标和,则递归到下一个元素。否则,我们尝试将当前元素添加到第二个子集中。如果我们功地将所有元素分配到了各个子集中,并且每个子集的和都等于目标和,则我们找到了一个解。 注意,在回溯过程中,我们需要使用subsets[:]而不是subsets来将当前子集的副本添加到结果列表中,因为subsets会在回溯过程中不断地修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值