最新的一個面試的算法題目——一個完全揹包問題

一個集合x有都不相同的n個元素,使用這個集合中的不定個數的元素,組成一個和爲s的序列,求出所有符合的序列,元素可以重複使用,只要元素的個數相同不考慮順序。

比如集合是x={2,3,4,5,7}; n=5, s=12可以得出以下的序列:

2 2 2 2 2 2

2 2 2 3 3

3 3 3 3

2 2 2 2 4

2 3 3 4

2 2 4 4

4 4 4

2 2 3 5

3 4 5

2 5 5

2 3 7

5 7

解題思路:

元素存在arr數組中,大小爲size。

path[i][j]表示數組前i個元素(一個元素可出現多次),組成重量恰好爲j裝入揹包的可能,如可能則爲true,否則爲false。得到遞推關係:                

                path[i][j]=true,if path[i][j-arr[i]]=true的話;否則爲false。

邊界條件path[i][0]=true(i爲1...size),爲了避免遍歷,在程序中使用hashtable數組,如果可以通過前面的元素組成容量爲j的揹包,則hashtable[j]=true,否則爲false。

  0 1 2 3 4 5 6 7 8 9 10 11 12
1 T F T T T F T F T F T F T
2 T F F T F T T T T T T T T
3 T F F F T F T T T T T T T
4 T F F F F T F T T T T T T
5 T F F F F F F T T T T T T

求出path數組後,通過printPath(int i,int j,int c)函數打印出所有的路徑。printPath(int i,int j,int c)是個遞歸函數,表示打印出所有第i個元素和前i-1個元素組成容量爲j的揹包的路徑,函數遞歸過程中使用v隊列保存過程中的元素,其中c爲v隊列的大小。如printPath(2,12,0)將arr[2]=3加入隊列,然後調用printPath(2,9,1)將3入隊,然後分兩路,一路如字所示,調用printPath(1,6,2)將2入隊,...,隊列中元素爲3,3,2,2,2,逆序打印;一路如淺紅色所示,調用printPath(2,6,2)將3入隊,...,隊列中元素3,3,3,3,逆序打印。調用遞歸函數請讀者仔細體會。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <memory.h>
#define _DEBUG 1
#define MAXM 20
#define MAXN 10
int arr[MAXN];//數數組
bool path[MAXN][MAXM+1];//路線
bool hashtable[MAXM+1];//是否存在
int c;//記錄打印隊列大小
int v[MAXM+1];//打印隊列,數組大小最大爲MAXN+1,此時每個數爲1

void printPath(int i,int j,int c){
	if(path[i][j] &&j-arr[i]==0){//第一個點
		printf("%d ",arr[i]);
		for(int t=c-1;t>0;t--){
			printf("%d ",v[t]);
		}
		printf("%d\n",v[0]);
		return;
	}
	assert(path[i][j]);
	v[c++]=arr[i];//將元素加入到打印隊列中
	for(int k=1;k<=i;k++){
		if(path[k][j-arr[i]])
			printPath(k,j-arr[i],c);		
	}
	c--;//將元素從打印隊列中刪除
}

void solve(int size,int m){//size表示數組的大小,從1開始
	int i,j;
	//初始化
	memset(path,false,sizeof(path));
	hashtable[0]=true;	
	for(i=1;i<=size;i++){
		for(j=arr[i];j<=m;j++){
			if(hashtable[j-arr[i]]){
				hashtable[j]=true;
				path[i][j]=true;
			}
		}
	}
	for(i=1;i<=size;i++){
		if(path[i][m]){
			printPath(i,m,0);
		}
	}
}
int main(){
#if _DEBUG==1
	freopen("interview.in","r",stdin);
	freopen("interview.out","w",stdout);
#endif

	int n,m;
	int i,j;

	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d",&arr[i]);
	}
	solve(n,m);
	return 0;
}


 

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