打怪兽问题
给你两个数组:一个是怪兽能力数组,一个是钱的数组(获取怪兽的能力要的钱)
6 9 3。。。
3 5 100。。。
只能通过用钱才能买到这个怪兽的能力,开始的能力是0
只能通过贿赂怪兽才能累加这个怪兽的能力,
目标是来到0-N-1号怪兽并且通关,最少花多少钱?
如何通关?当你来到i号怪兽时如果你累加的能力不如怪兽的能力,你是一定要贿赂它的
如果你的能力大于它的,你可以选择贿赂它也可以选择不贿赂它。
要用最少的钱通关
有选择问题:
5 4 3 8
1 1 20 100W
0 1 2 3
花的最少钱是2
可以用两个动态规划来解
i位置代表怪兽,列代表能力
100W 5
1 20W
0 1 2 3 4 5 100W
0 -1 1
1
dp[i][j]
arr[0....i]
小人从0号怪兽达i号怪兽,能力必须达到j时,至少要花多少钱?如果没有达到,花的最少钱数标为-1
遍历最后一行的含义是:我到达n-1号怪兽时能力达到0,1,2,3,4。。。j 时花多少钱
找到最后行最小的正数就是我要的答案
怎么去转移这个dp[i][j]


求最小值是,-1不要用
第一行怎么填?
dp[0][ability[0]]=money[0]
其它位置都是-1
第一列怎么填?
dp[i][0]不用填,因为都是正数数组,所有都组不成0号能力出来
看依赖关系,普遍位置只依赖上一行
普通位置怎么填?
dp[i][j]
当j<ability[i] 则组不成有效值,因为到i号怪兽时,之前累加的能力不足以通关
当j>=ability[j] 则有两个选择,
不选I号怪兽:dp[i][j]=dp[i-1][j]
选i号怪兽:dp[i][j]=dp[i-1][j-ability[j]]+money[i]
在以上选择都效的情况下,选最小值
public static int minMoney(int[] ability,int[] money){
if(ability==null||ability.length==0||money==null||money.length==0){
return 0;
}
if(ability.length!=money.length){
return 0;
}
//dp[i][j] 0...i ability->j min money
//dp[i][j] dp[i-1][j] 0...i-1 ability->j min money no
//dp[i][j] dp[i-1][j-ability[i]] + money[i] yes
//min
//yes ,no condition j>=ability[i] p1=yes,p2=no min
//j<ability[i] -1
//dp[0][ability[0]]=money[0]
//dp[][0]=-1
int N=ability.length;
int sum=0;
for(int a:ability){
sum+=a;
}
int[][] dp=new int[N][sum+1];
for(int i=0;i<N;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=-1;
}
}
dp[0][ability[0]]=money[0];
for(int i=1;i<N;i++){
for(int j=1;j<=sum;j++){
if(j<ability[i]){
dp[i][j]=-1;
}else{//j>=ability[i]
//第一种方案
int p1=dp[i-1][j];
//第二种方案
int p2=-1;
if(dp[i-1][j-ability[i]]!=-1){
p2=dp[i-1][j-ability[j]]+money[i];
}
if(p1!=-1 && p2!=-1){
dp[i][j]=Math.min(p1,p2);
}
if(p1==-1){
dp[i][j]=p2;
}
if(p2==-1){
dp[i][j]=p1;
}
}
}
}
//最后一行的最小的正数值就是答案
//n-1 min e -1
int minMoney=Integer.MAX_VALUE;
boolean flag=false;
for(int i=0;i<=sum;i++){
if(dp[N-1][i]!=-1){
minMoney=Math.min(minMoney,dp[N-1][i]);
flag=true;
}
}
return flag?minMoney:0;
}
但是这个尝试方案有一个问题是:怪兽的能力非常大时,这种尝试是行不通的 0-10^6
这种方案只适合单个怪兽能力不大的情况下使用
都要考虑表的大小
所以如果怪兽的能力值范围非常大,钱的数量不大时,用另一种动态规划的尝试
i,j
dp[i][j]通关能力的最大能力值
arr[o...i] 从0号一路通关到I号怪兽时,有形成的钱数必须到达j时,所达到的最大怪兽能力
如时花这么多钱时,没有方案,则为-1,如有方案,则为能力值

第一行怎么求?dp[0][money[0]]=ability[0]
第一列怎么求?dp[i][0]=-1,不用求,因为你只有0个钱的话,你搞不定任何一个怪数(前提是正数数组)
dp[i][j]怎么求?
dp[i-1][j] 有方案,表示用j个钱是可以搞定0-i-1的怪兽的
如果些时的能力值大于ability[i]的话,我的可以不贿赂你
p1=dp[i-1][j]
否则的话,必须选当前怪兽
条件是 j-money[i]>0 && dp[i-1][j-money[i]]!=-1
p2=dp[i-1][j-money[i]]+ability[i]
Math.max(p1,p2);
答案是最后一行从左往右遍历第一个不为-1的值
1、用怪兽的个数当行,钱数当列
public static int minMoney(int[] ability,int[] money){
if(ability==null || ability.length==0 || money==null || money.length==0){
return 0;
}
if(ability.length!=money.length){
return 0;
}
//dp[i][j] 0...i money->j max ability
//dp[i-1][j] 0...i-1 money->j max ability no dp[i-1][j] >=ability[i]
//dp[i-1][j-money[i]] (!=-1) + ability j-money[i]>=0 or -1 yes
//no,yes max
int sum = 0;
for (int num : money) {
sum += num;
}
// dp[i][j]含义:
// 能经过0~i的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少?
// 如果dp[i][j]==-1,表示经过0~i的怪兽,花钱为j是无法通过的,
// 或者之前的钱怎么组合也得不到正好为j的钱数
int[][] dp = new int[ability.length][sum + 1];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = -1;
}
}
// 经过0~i的怪兽,花钱数一定为money[0],达到武力值ability[0]的地步。
// 其他第0行的状态一律是无效的
dp[0][money[0]] = ability[0];
for (int i = 1; i < ability.length; i++) {
for (int j = 0; j <= sum; j++) {
// 可能性一,为当前怪兽花钱
// 存在条件:
// j - money[i]要不越界,并且在钱数为j - money[i]时,
// 要能通过0~i-1的怪兽,并且钱数组合是有效的。
if (j - money[i]>=0 && dp[i - 1][j - money[i]] != -1) {
dp[i][j] = dp[i - 1][j - money[i]] + ability[i];
}
// 可能性二,不为当前怪兽花钱
// 存在条件:
// 0~i-1怪兽在花钱为j的情况下,能保证通过当前i位置的怪兽
if (dp[i - 1][j] >= ability[i]) {
// 两种可能性中,选武力值最大的
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
}
}
}
int ans = 0;
// dp表最后一行上,dp[N-1][j]代表:
// 能经过0~N-1的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少?
// 那么最后一行上,最左侧的不为-1的列数(j),就是答案
for (int j = 0; j <= sum; j++) {
if (dp[ability.length - 1][j] != -1) {
ans = j;
break;
}
}
return ans;
}

浙公网安备 33010602011771号