题目:
小易给定你一个长度为\mathit nn的正整数序列A_{i}A i ,你每次可以使用\text 11的代价将某个数加一或者减一,你希望用最少的代价使得所有数的乘积等于\mathit BB,求最小代价(操作结束后每个数也必须是正整数)。
思路:
依旧是动态规划,这个比较复杂。
目标是求得最小值,思想是遍历所有两数相乘可以得到目标值的情况,不断比较得到最小值。
返回目标值,将最小值赋值给它并返回。
动态数组初始化第一行和第一列
第一行是一个数字的情况下,需要几次操作能变成列对应值
第一列是乘积为1的情况下,需要的操作值。
从目标节点开始递归,不断向小节点开始规划,
每当发现一个能作为因子的数,就进行递归,递归的下一次参数中(使用的数字减一,目标值为另一个因子,两个数组值依旧)。
(暂时的最小值)这轮递归的值为该数字变为因子的所需操作数加上递归另一个因子的值。
代码:
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int target = scanner.nextInt();
int[] h = new int[n];
for(int i = 0; i < n; i++) {
h[i] = scanner.nextInt();
}
//dp[i][j] 表示前i个数字 使得为j 所需要的代价
Integer[][] dp = new Integer[n+1][target + 1];
int sum = 0;
for(int i = 1; i <= n; i++) {
sum += h[i-1] - 1;
dp[i][1] = sum;
}
for (int j = 1; j <= target; j++) {
dp[1][j] = Math.abs(j - h[1-1]);
}
int result = doCheck(dp, n, target, h);
System.out.println(result);
}
private static int doCheck(Integer[][] dp, int n, int target, int[] h) {
if (dp[n][target] != null) {
return dp[n][target];
}
int min = Integer.MAX_VALUE;
for (int i = 1; i <= target; i++) {
if (target % i != 0) {
continue;
}
int j = target / i;
min = Math.min(min, doCheck(dp, n-1, j, h) + Math.abs(h[n-1] - i));
}
return dp[n][target] = min;
}
}