2021.06.03邮票面值设计
题目描述
给定一个信封,最多只允许粘贴 N 张邮票,计算在给定 K(N+K≤15)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值 MAX,使在 1 至 MAX 之间的每一个邮资值都能得到。
例如,N=3,K=2,如果面值分别为 1 分、4 分,则在 1∼6 分之间的每一个邮资值都能得到(当然还有 8 分、9 分和 12 分);如果面值分别为 1 分、3 分,则在 1∼7 分之间的每一个邮资值都能得到。可以验证当 N=3,K=2 时,7 分就是可以得到的连续的邮资最大值,所以 MAX=7,面值分别为 1 分、3 分。
输入格式
2 个整数,代表 N,K。
输出格式
输出共 2 行。
第一行输出若干个数字,表示选择的面值,从小到大排序。
第二行,输出 MAX=S,S 表示最大的面值。
样例输入
3 2
样例输出
1 3
MAX=7
思路:dfs+dp
- 如果邮票面值固定,可以简单想到通过以下dp求解:
(1)状态:dp[i]: 可以凑出i的最少有票数。
(2)转移方程:dp[i] = min{ dp[i], dp[i-val[j]+1 }
(3)停止条件:dp[i] > n - 但是,本题要求我们设计邮票的面值,所以,可以通过如下方法:
将dp放入dfs的fit函数中去 - 通过模拟样例发现深搜的限定规则:
如果val[i]表示第i个邮票的面值,可以得到
val[i+1]∈[ val[i]+1, val[i]*n+1]
因为每种邮票的面值必须是递增的(否则肯定不能使max最大),并且,如果超过了val[i]*n+1,那么表示的面值就不连续了。
代码
class Solution{
int k, n;
int res = 0;
int[] val = new int[17]; //每种邮票的面值
int[] ans = new int[17];
int[] dp = new int[5000];
void test() {
Scanner cin = new Scanner(System.in);
n = cin.nextInt();
k = cin.nextInt();
//定义状态 dp[i]: 可以表示i的最少个数 dp[i] = min{ dp[i-val[j]] +1 } 其中j:0~len
val[1] = 1;
dfs(1);
for(int i = 1; i <= k; i++) System.out.print(ans[i]+" ");
System.out.println();
System.out.println("MAX="+res);
}
//深度搜索,val[i+1]: val[i]+1 ~ val[i]*n+1: cur表示当前已有个数
void dfs(int cur) {
if(cur == k) {
int i = 0;
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
while(dp[i] <= n) {
i++;
for(int j = 1; j <= k && i-val[j] >= 0; j++) {
dp[i] = Math.min(dp[i], dp[i-val[j]]+1);
}
}
if(i-1 > res) {
ans = Arrays.copyOf(val, val.length);
res = i-1;
}
return;
}
for(int k = val[cur]+1; k <= val[cur]*n+1; k++) {
val[cur+1] = k;
dfs(cur+1);
}
}
}