原题来源于科大讯飞的一道在线编程题
作者:知足常乐_健康第一
链接:https://www.nowcoder.com/discuss/76153
来源:牛客网
小明和小红是好朋友,但最近遇到一个棘手的问题,有一盒糖果要分成两份但是每颗糖果质量都不尽相同,但为了分配的公平每份糖的糖果数量相差不得超过1,在此条件下两份糖果的质量差距尽可能小。
输入:一行数,包含一个数n,代表糖果数量,后面一次是n个整数一次表示每个糖果的质量,每个糖果的质量都是1到450之间的一个整数,每盒最多有20个糖果。
输出:每个样例输出两个数字分别为两堆糖果的质量,如不相同,先小后大。
样例
输入:5 9 6 5 8 7
输出:17 18
一开始做这道题的时候,我纠结于“找到一个策略,按照这个策略分糖果就能得到结果”。但后来我发现,其实根本不需要找出这么个策略,只需用暴力搜索算法即可。关键点是:“每份糖的糖果数量相差不得超过1”,假设糖果数目是n,则分成的两堆糖果的数量一定是n/2和n-n/2。这就归结为一个组合问题:“如何在n个糖果中选n/2个糖果,使得选出的糖果堆和剩下的糖果堆质量之差最小?”
因此,只要搜索所有C(n,n/2)种分糖方法,就能找出差值最小的那种。
这里重点解释下search函数:
第一个参数sugers,表示所有糖的质量。
第二个参数weight,表示当搜索到这一步时,已经选取出来的糖果的质量总和。
第三个参数start,表示“决定要不要选”的那颗糖果在sugers中的编号。
第四个参数count,我手里这堆糖果中糖果的数量。如果count到达了n/2,表明搜索到一种分法,可以进行比较了。
第五个参数res,表示“当前搜索过的所有分法中最好的分法”,当搜索完后,就是结果。
第六个参数target,是糖果的总重量/2。
第七个参数minchazhi,表示“当前搜索过的所有分法中最好的分法与target的差值”,是分法好坏的依据。
最后两个,sum是糖果的总重量,n是糖果的总数。
#include<iostream> #include<vector> #include<algorithm> #include<math.h> #include<limits.h> using namespace std; void search(const vector<int> &sugers, int weight, int start, int count, int &res,int target,int& minchazhi,int sum,int n) { //用搜索法 //我自己在糖果堆中选糖果,对于每一颗糖果,我可以选或者不选 //如果选,count+1,表示我当前手里的糖果的数量 //每次选完,我手里的糖果达到总数量的一半后,就进行手里糖果重量与总质量一半的最小差值minchazhi的更新 //并记录在当前最小差值下的我手里糖果的质量,最后的res就是结果 if (count == n/2) { if (abs(2 * weight - sum) < minchazhi) { //这次搜索的结果比上次更好,用res记录这个更好的结果 res = weight; minchazhi = abs(2 * weight - sum); } } else { if (start < sugers.size()) { //还可以继续搜索 search(sugers,weight, start + 1, count, res, target, minchazhi, sum,n); search(sugers, weight+sugers[start], start + 1, count+1, res, target, minchazhi, sum,n); } } } int main() { int n; while (cin >> n) { vector<int> sugers(n); int sum = 0; for (int i = 0; i < n; i++) { cin >> sugers[i]; sum += sugers[i]; } int target = sum / 2; int res = 0, minchazhi = INT_MAX; search(sugers, 0, 0, 0,res,target,minchazhi,sum,n); if(res>sum-res) cout <<sum-res << ' ' << res << endl; else cout <<res << ' ' << sum-res << endl; } return 0; }