網易2015年 任2n個整數,從其中選出n個整數,使得選出的n個整數和同剩下的n個整數之和的差最小。

一道網易2015年的內推筆試題實現採用java和c++ ,超詳解,外加推理理解;

思路:可以考慮選出的在2n個數中找到n個數使的這n個數的和接近2n個數的總和的1/2.所以我們可以計算出所有n個數有可能的值!

    我們有了上述思路,就可以考慮建立個二維數組int flag[n][m].參數本身可以取0和1,意思是如果存在2n個數中存在n個數的和爲m ,則將其置爲1;

    首先我們知道flag[0][0]=1;重點是如何一個個推出所有的flag[n][m];

    舉個例子flag[1][10];我們就可以在2n中找到1個爲10的數就可以了。接着我們要找

    flag[2][14]我們就可以用已有的flag[1][10]推出,在所有的數組中如果能找到一個數num[i]使得flag[2-1][14-num[i]]等於1就可以確定flag[2][14]爲1;

    所以我們就可以推廣開來。

    if(k>num[i-1]&&flag[j-1][k-num[i-1]]==1){//這裏k>num[i-1]是因爲我要確保下標不爲負!這裏用i-1看下面java代碼上註釋

        flag[j][k]=1;

    }


    

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;
            }
        }
    }

}

如果有什麼疑問,或是本文有不對之處敬請指出!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章