將求最大的連續組合值轉換爲揹包問題

題目:有四種面值的郵票很多枚,這4種郵票面值分別爲1分,4分,12分,21分。現在從多張中最多任取5張進行組合,求出這些郵票的最大連續組合值。(這些郵票最小能表示的面值爲1,那麼最大連續組合值的意思是說,從1開始往下一直連續下去,當且僅當該面值能被這4種郵票表示。)

書上給了一個參考程序:

package com.interview.mccv;


public class ProgramInbook {
	int num = 5;
	int k;
	boolean find;
	int logo[] = new int[num];
	int stamp[] = { 0, 1, 4, 12, 21 };
	int M = 5;// 表示stamp數組的長度


	private boolean check(int n, int Value) {
		if (n >= 0 && Value == 0) {
			find = true;
			int sum = 0;
			for (int i = 0; i < num && logo[i] != 0; i++) {
				sum += stamp[logo[i]];
				// System.out.print(Stamp[Logo[i]] + ",");
			}
			// System.out.println("總數爲:" + Sum);
		} else
			for (int i = 1; i < M && !find && n > 0; i++)
				if (Value - stamp[i] >= 0) {
					logo[k++] = i;
					check(n - 1, Value - stamp[i]);
					logo[--k] = 0;
				}
		return find;
	}


	public int getResult() {
		int i;
		for (i = 1; check(num, i); i++, find = false)
			;
		return i - 1;
	}


	public static void main(String[] args) {
		// 時間測試
		long start = System.currentTimeMillis();
		ProgramInbook program = new ProgramInbook();
		System.out.println(program.getResult());
		long end = System.currentTimeMillis();
		System.out.println("ProgramInbook time lasts " + (end - start) + "ms");
	}
}



思路就是從1開始往下遍歷,對於每一個數字看能不能被這4種郵票表示,知道有一個數字不能被表示,那麼程序停止。
程序輸出爲:
1,總數爲:1

1,1,總數爲:2

1,1,1,總數爲:3

1,1,1,1,總數爲:4

1,1,1,1,1,總數爲:5

1,1,4,總數爲:6

1,1,1,4,總數爲:7

1,1,1,1,4,總數爲:8

1,4,4,總數爲:9

1,1,4,4,總數爲:10

1,1,1,4,4,總數爲:11

4,4,4,總數爲:12

1,4,4,4,總數爲:13

1,1,4,4,4,總數爲:14

1,1,1,12,總數爲:15

............

4,4,21,21,21,總數爲:71
前些天做過完全揹包的問題,貌似這題可以用類似(只是說和完全揹包類似,不完全一樣)的方法解決。如果不清楚揹包問題,可以看一下這個:http://blog.csdn.net/wangxiaolongbob/article/details/8037720
將上面的問題轉化一下:
有一個揹包負重爲105(針對本題),現在有4個物品{1,4,12,21},每個物品的個數是無限個,求物品剛好裝滿揹包(物品總重量爲105)時的物品個數最小爲多少?
當然這個問題不一定能求出來,但是當以這個問題爲目標,最後遍歷玩二維數組以後,那麼郵票的問題也就解決了。



如上表所示,

初始化狀態爲全都“不能表示”。
接下來用物體1來,表示1需要1個,表示2需要2個,表示3就需要3個...他不能表示6,因爲物體最多隻能5個,那麼後面的全是無窮大了。
接下來用4來表示,他不能表示1,2,3,只能從4開始,那麼當前狀態1,2,3的最優值就是前面狀態的最優值,現在注意了,用4這個物體去表示4只需要1個,比前一個狀態(物體1表示4需要4個)要少,那麼就替換掉了。然後看用4這個物體去表示5,發現5-4=1,那麼我們看第一列發現他的最優解就是1,那麼5的最優解就是2了。其他的就按照這個方法填就是了,在填的時候要始終判斷個數是不是大於5,如果大於5,就說明不能表示,就用無窮大表示。當我們把這個二維數組遍歷完了之後,在去遍歷最後一排(21這一排),直到遇到無窮大就停下來,這時結果就找到了。
當然,這個二維數組可以優化成用一維數組表示,代碼如下:

package com.interview.mccv;

/**
 * 最大連續組合值 Maximum continuous composite value
 * 
 * @author xiaolong
 * 
 */
public class MCCV {
	private int maxElementNum;// 一個組合中元素的最多個數
	private int[] faceValue;// 每一個元素的價值存放在數組中
	private int[] record;
	private int max;// faceValue中的最大值
	private int min;// faceValue中的最小值

	public MCCV(int maxElementNum, int[] faceValue) {
		this.maxElementNum = maxElementNum;
		this.faceValue = faceValue;
		initRecord();
	}

	private void initRecord() {
		sort(faceValue);
		min = faceValue[0];
		max = faceValue[faceValue.length - 1];
		record = new int[max * maxElementNum + 1];// 把faceValue所能表示的最大值作爲揹包的總負重,在這題中最大值爲21*5=105
		for (int i = 0; i < record.length; i++) {
			record[i] = Integer.MAX_VALUE;
		}
	}

	/**
	 * 對faceValue進行排序
	 */
	private void sort(int[] faceValue) {
		int minIndex;
		for (int i = 0; i < faceValue.length; i++) {
			minIndex = i;
			for (int j = i + 1; j < faceValue.length; j++) {
				if (faceValue[j] < faceValue[minIndex])
					minIndex = j;
			}
			if (minIndex != i)
				swap(i, minIndex);
		}
	}

	/**
	 * 在faceValue數組中交換下標分別爲i和j的2個數的位置
	 * 
	 * @param i
	 * @param j
	 */
	private void swap(int i, int j) {
		int temp;
		temp = faceValue[j];
		faceValue[j] = faceValue[i];
		faceValue[i] = temp;
	}

	public void start() {
		int temp;
		for (int i = 0; i < faceValue.length; i++) {// 遍歷所有的faceValue,相當與揹包問題中遍歷所有的物品
			for (int j = faceValue[i]; j < record.length; j++) {// 從當前物品的面值開始,一直遍歷到最後。
				if ((j - faceValue[i]) == 0) {
					record[j] = 1;
					// System.out.print(j + ":");
				} else {
					if ((temp = record[j - faceValue[i]]) != Integer.MAX_VALUE) {
						temp++;
						if (temp <= maxElementNum && temp < record[j])// 只有噹噹前狀態比前一狀態小時,才記錄
							record[j] = temp;
					}
				}
				// System.out.print(record[j] + " ");
			}
			// System.out.println();
		}
	}

	public int getResult() {
		for (int i = min; i < record.length; i++) {
			// System.out.println(record[i]);
			if (record[i] == Integer.MAX_VALUE) {
				return i - 1;
			}
		}
		return 0;
	}

	public static void main(String[] args) {
		int[] faceValue = new int[] { 1, 4, 12, 21 };
		int maxElementNum = 5;

		// 時間測試
		long start = System.currentTimeMillis();
		MCCV mccv = new MCCV(maxElementNum, faceValue);
		mccv.start();
		System.out.println("最大連續組合數爲:" + mccv.getResult());
		long end = System.currentTimeMillis();
		System.out.println("MCCV time lasts " + (end - start) + "ms");

	}
}



這個過程描述的比較生硬,以後要多寫寫了。

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