一道網易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; } } } }
若是有什麼疑問,或是本文有不對之處敬請指出!