解题代码:
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 + 剪枝
关键在剪枝
- 最多拿四张邮票,同面额的邮票种类超过5,则按5计算
- 顾客持有邮票张数不超过4
- 不重复搜索比当前面值小的邮票,同时避免错误的tie(i=pre)
- 减掉比当前面值大的邮票,排序的好处
小技巧:种类 > 张数 > 最大面值
评价分数 = 种类数 * 100 + (4 - 邮票数) * 10 + 最大面值;