个人笔记:算法讲座3.6——血和蓝(二维背包)

仅供参考学习使用,谢谢

问题描述:

贫瘠之地上有很多怪,战胜它们会获得它们身上的金币,不过跟它们战斗的勇士也会受到攻
击,损失一定的血和蓝,无论血和蓝哪个没有了,勇士都会挂掉。Alice在这里打怪,她不想
挂掉,那她最多可以赚到多少金币呢?

  • 输入:

第一行有三个数,Alice的血量和蓝量、怪的个数。第二行开始,每行三个数,是与每
个怪决斗会损失的血量和蓝量,以及获得的金币数。(所有数都是自然数)

  • 输出:

Alice能够获得的最大金币数。

思路分析:

二维背包问题(多加一个01背包即可)
01背包问题的状态转换方程:
f[i][v1][v2] = Max{f[i-1][v1][v2], f[i-1][v1-c1[i]][v2-c2[i]]+w[i]}
该问题是一个01背包问题的简单变形,首先要将这个问题分成子问题,然后优化子结构。
根据01背包问题的思想,按照顺序打N个怪物,且逐个考虑 血和蓝 为11~RedBlue每个点所能产生的最大价值
这样每个点 Red*Blue内所能产生的最大价值仅和上一个怪物的产生价值有关,即满足了 无后效性 和 逆向组解
考虑某红蓝值时 某怪物的最大价值 需要考虑
a=该红蓝值对应点所含红蓝值 减去 当前怪物所消耗的红蓝 在前一个怪物所产生的最大价值+当前怪物所能产生的价值
b=该红蓝值对应点 在上一个怪物所能产生的最大价值
如果a>b则该时间段在该怪物所能产生的最大价值是情况a所能产生的最大价值,否则是情况b所能产生的最大价值

算法描述:

1. typedef

需要一个int型一维数组red用于记录每一个怪物所消耗的红,
一个int型一维数组blue用于记录每一个怪物所消耗的蓝,
一个int型一维数组value用于记录每一个怪物所能产生价值,
一个int型二维数组f用于记录在 每一个怪物 某一红蓝值 所能产生的最大价值

2. init函数来初始化

由于该问题需要逆向组解,即先将f的最后一行(从最后一个金矿向第一个金矿移动)
最后一个金矿的每一个时间段所能产生的最大价值进行输入,同时需要将f[i][0][0],time[0],value[0]初始化为0

3. workout函数

计算每一个怪物 每一点对应红蓝值 所能产生的最大价值 并填入f表中
从倒数第二个怪物开始移动 可以使用的总红蓝 每多出一点红一点蓝 就要考虑当前时间段是否已经达到这个怪物所要消耗的红蓝值 如果已经达到则需要判断:
a=该红蓝值对应点所含红蓝值 减去 当前怪物所消耗的红蓝 在前一个怪物所产生的最大价值+当前怪物所能产生的价值
b=该红蓝值对应点 在上一个怪物所能产生的最大价值
如果a>b则该时间段在该怪物所能产生的最大价值是情况a所能产生的最大价值,否则是情况b所能产生的最大价值

测试数据:

  • 数据1:

100 10 4
101 2 88
20 7 36
40 1 23
10 5 30

  • 结果1

59

  • 数据2:

100 30 6
91 5 90
48 16 72
36 17 70
55 3 60
8 40 99
10 14 23

  • 结果2

142

#include <stdio.h>
#define maxsize 1000

typedef struct {
    int red[maxsize+1];
    int blue[maxsize+1];
    int value[maxsize+1];
    int f[maxsize+1][maxsize+1];//N,red,blue
}Bag;

Bag create(int Red, int Blue, int N){
    Bag Alice;
    //初始化 f[i][j]
    Alice.red[0]=0;
    Alice.blue[0]=0;
    Alice.value[0]=0;
    
    for (int x=0; x<Red+1; x++)
        for (int y=0; y<Blue+1; y++)
            Alice.f[x][y]=0;
    
    //给 red[] & blue[] & value[] 赋值
    for (int i=1; i<=N; i++) {
        int red=0,blue=0,value=0;
        scanf("%d%d%d",&red,&blue,&value);
        Alice.red[i]=red;
        Alice.blue[i]=blue;
        Alice.value[i]=value;
    }
        
    for (int z=1; z<=N; z++){
        for (int x=Red; x>=Alice.red[z]; x--) {//这里是为了避免成为二维完全背包,所以 -- 而不是 ++
            for (int y=Blue; y>=Alice.blue[z]; y--) {
                if (Alice.f[x][y]>Alice.f[x-Alice.red[z]][y-Alice.blue[z]]+Alice.value[z]) {
                    Alice.f[x][y]=Alice.f[x][y];
                } else {
                    Alice.f[x][y]=Alice.f[x-Alice.red[z]][y-Alice.blue[z]]+Alice.value[z];
                }
            }
        }
    }
    
//    for (int x=1; x<=Red; x++) {
//        for (int y=1; y<=Blue; y++) {
//            printf("%3d",Alice.f[x][y]);
//        }
//        printf("\n");
//    }
    
    return Alice;
}

Bag workout (int Red, int Blue, int N, Bag Alice){
    for (int z=1; z<=N; z++){
        for (int x=Red; x>=Alice.red[z]; x--) {
            for (int y=Blue; y>=Alice.blue[z]; y--) {
                if (Alice.f[x][y]>Alice.f[x-Alice.red[z]][y-Alice.blue[z]]+Alice.value[z]) {
                    Alice.f[x][y]=Alice.f[x][y];
                } else {
                    Alice.f[x][y]=Alice.f[x-Alice.red[z]][y-Alice.blue[z]]+Alice.value[z];
                }
            }
        }
    }
    return Alice;
}

int main(int argc, const char * argv[]) {
    int Red=0,Blue=0,N=0;//Red&Blue的含义在背包问题中等价于背包总承重,N相当于怪物个数
    scanf("%d%d%d",&Red,&Blue,&N);
    
    Bag Alice=create(Red, Blue, N);
//    Alice=workout(Red, Blue, N, Alice);
    
    printf("%d\n",Alice.f[Red][Blue]);
    
    return 0;
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值