ACM —— 1010 STAMPS

解题代码:

import java.util.Arrays;
import java.util.Scanner;

public class Main {

	private static int[] mValues;
	private static int[] mTime;
	private static int[] mSolve;
	private static int[] mBestSolve;//solve[4]:邮票张数  solve[5]:邮票种数  solve[0..3]:持有的邮票面值,0表示不持有
	private static int tempScore;
	private static boolean flagTie;
	
	public static void main(String[] args) {
		Scanner stdin = new Scanner(System.in);
		while (stdin.hasNextLine()) {
			int[] values = new int[100];
			int[] type = new int[26];
			int length = 0;
			int temp;
			int count = 0;
			int cusRequests;
			while ((temp = stdin.nextInt()) != 0) {
				if (type[temp] < 5) { //剪枝,同面额的邮票种类超过5,则按5计算
					type[temp]++;
					values[length++] = temp;
				}
			}
			while ((cusRequests = stdin.nextInt()) != 0) {
				getBestByDFS(cusRequests, values,length);
				outBestSolve(cusRequests);
			}
			
		}
	}

	private static void getBestByDFS(int cusRequests, int[] values, int length) {
		init(values,length);
		dfs(cusRequests, 0, 0, 0);
	}

	private static void init(int[] values, int length) {
		mValues = new int[length];
		mTime = new int[length];
		mSolve = new int[5];
		mBestSolve = new int[6];
		tempScore = 0;
		flagTie = false;
		System.arraycopy(values, 0, mValues, 0, length);
		Arrays.sort(mValues);
	}

	private static void outBestSolve(int cusRequests) {
		if (mBestSolve[4] == 0) {
			System.out.println(cusRequests + " ---- none");
		} else if (flagTie) {
			System.out.println(cusRequests + " (" + mBestSolve[5] + "): tie");
		} else {
			System.out.print(cusRequests + " (" + mBestSolve[5] + "):");
			for(int i = 0; i < 4; i++) {
				if (mBestSolve[i] == 0) {
					break;
				}
				System.out.print(" " + mBestSolve[i]);
			}
			System.out.println();
		}
	}

	/**
	 * 
	 * @param need 总面值
	 * @param num 邮票张数
	 * @param type 邮票种数
	 * @param pre 剪枝
	 * @return
	 */
	private static void dfs(int need, int num, int type, int pre) {

		if (num == 5) { //剪枝,顾客持有邮票张数不超过4   
			return;
		}
		
		if (need == 0) {
			int score = type*100 + (4 - num)*10 + getMax();
			if (score == tempScore) {
				flagTie = true;
			} else if (score > tempScore){
				flagTie = false;
				tempScore = score;
				mBestSolve[4] = num;
				mBestSolve[5] = type;
				for (int i = 0; i < 4; i++) {
					mBestSolve[i] = mSolve[i];
				}
			}
			return;
		}
		
		for (int i = pre; i < mValues.length; i++) { //i=pre 剪枝,不重复搜索比当前面值小的邮票,同时避免错误的tie 
			if (need < mValues[i]) {//剪枝, 减掉比当前面值大的邮票,排序的好处
				return;
			}
			mSolve[num++] = mValues[i];
			if (mTime[i] != 0) {
				mTime[i]++;
				dfs((need - mValues[i]), num, type, i);
			} else {
				mTime[i]++;
				dfs((need - mValues[i]), num, type+1, i);
			}
			mSolve[--num]=0;  //回溯   
			mTime[i]--;  
		}
		
		return ;
	}

	private static int getMax() {
		int a = mSolve[1] > mSolve[2] ? mSolve[1]:mSolve[2];
		int b = mSolve[3] > mSolve[4] ? mSolve[3]:mSolve[4];
		return (a > b ? a:b);
	}

}


 


DFS + 剪枝

关键在剪枝

  1. 最多拿四张邮票,同面额的邮票种类超过5,则按5计算
  2. 顾客持有邮票张数不超过4
  3. 不重复搜索比当前面值小的邮票,同时避免错误的tie(i=pre)
  4. 减掉比当前面值大的邮票,排序的好处

小技巧:种类 > 张数 > 最大面值

评价分数 = 种类数 * 100 + (4 - 邮票数) * 10 + 最大面值;

猜你喜欢

转载自blog.csdn.net/WYYZ5/article/details/48369529