一道网易2015年的内推笔试题实现采用java和c++ ,超详解,外加推理理解;java
思路:能够考虑选出的在2n个数中找到n个数使的这n个数的和接近2n个数的总和的1/2.因此咱们能够计算出全部n个数有可能的值!c++
咱们有了上述思路,就能够考虑创建个二维数组int flag[n][m].参数自己能够取0和1,意思是若是存在2n个数中存在n个数的和为m ,则将其置为1;
数组
首先咱们知道flag[0][0]=1;重点是如何一个个推出全部的flag[n][m];ide
举个例子flag[1][10];咱们就能够在2n中找到1个为10的数就能够了。接着咱们要找spa
flag[2][14]咱们就能够用已有的flag[1][10]推出,在全部的数组中若是能找到一个数num[i]使得flag[2-1][14-num[i]]等于1就能够肯定flag[2][14]为1;orm
因此咱们就能够推广开来。it
if(k>num[i-1]&&flag[j-1][k-num[i-1]]==1){//这里k>num[i-1]是由于我要确保下标不为负!这里用i-1看下面java代码上注释io
flag[j][k]=1;
class
}
test
num[i]的取值也须要循环获取全部可能值。(这里i的取值应该是0-2n)
而后咱们能够看出这里j自己能够取任何值(这里由于咱们只须要算到n个值因此j取值在(i-n)这里为何是i-n呢由于咱们最大只须要到n,并且若是i没取那么多。就按把j=i,通俗话讲就是,你总共的数只有i个,你怎么算其中的i+1个数的和)因此须要个循环。。而后咱们还须要全部可能的k值(这里k取值在0 - sum/2,由于咱们取这个范围就是为了节省运算时间,不必算这么多咱们的最终目的就是算出n个取值的和,因此没就不必循环那么多了)。
根据上述详细分析咱们能够写出循环代码:纯手打并不保证出错,真正代码看下面贴出来的
for(int i=0;i<2*n;i++){//总共有i个数
for(int j=i>n?n:i;j>0;j++){//在i个数其中的 j个数;
for(int k=0;k<=sum/2;k++){//j个数总和的值
if(k>num[i]&&flag[j-1][k-num[i]]==1){
flag[j-1][k-num[i]]=1;
}
}
}
}
下面贴出个人C++代码:
#include<stdio.h> #include<stdlib.h> int main(){ int num[] = {1,2,3,4,5,6,7,10}; int sum = 0; int num_length=sizeof(num) / sizeof(num[0]); int n = num_length / 2; //n为总和2n的一半 for (int i = 0; i < num_length; i++){ sum = sum+num[i]; } /** *如下大段是动态建立一个二维数组。 */ int **flag; flag = new int*[n+1]; for (int i = 0; i < n+1; i++){ flag[i] = new int[sum/2+1]; } for (int i = 0; i < n + 1; i++){ for (int j = 0; j < sum/2+1; j++){ flag[i][j] = 0; } } flag[0][0] = 1; for (int i = 0; i < 2*n; i++){ //能够当成总共有i个数 for (int j = i>n ? n : i; j > 0; j--){//而后从中找j个 for (int k = 0; k <= sum / 2; k++){//找出全部可能的值 if (k >= num[i] && flag[j-1][k-num[i]]==1){ flag[j][k] = 1; } } } } for (int i = sum/2+1; i>0 ; i--){ if (flag[n][i] == 1){ if (2 * i >= sum){ printf("差值最小为:%d\n", (2*i - sum)); } if (2 * i < sum){ printf("差值最小为:%d\n", (sum - 2*i)); } break; } } system("pause"); }
下面贴出个人Java代码:
package faceTest; public class test1 { public static void main(String[] args) { int num[]={1,2,3,4,5,7,9,10}; int sum=0; for(int i=0;i<num.length;i++){ sum+=num[i]; } int n=num.length/2; boolean flag[][]=new boolean[n+1][sum/2+1]; for(int i=0;i<=n;i++){ for(int k=0;k<=sum/2;k++){ flag[i][k]=false; } } flag[0][0]=true; for(int i=0;i<=2*n;i++){ for(int j=i>n?n:i;j>0;j--){//这里i=0并无进去。由于j>0的判断。因此num[i-1]才是遍历全部的数! for(int k=0;k<=sum/2;k++){ if(num[i-1]<=k&&flag[j-1][k-num[i-1]]){//这里由于必须是i-1;只有这样才能访问全部num[i] flag[j][k]=true; } } } } for(int i=sum/2;i>0;i--){ if(flag[n][i]){ System.out.println("差值为:"+(sum-2*i)); break; } } } }
若是有什么疑问,或是本文有不对之处敬请指出!