有趣的地方

有趣的地方

递归法解决完全背包问题

完全背包问题作为01背包问题的升级版,我们可以通过修改01背包递归函数来求解完全背包问题

题目链接:3. 完全背包问题 - AcWing题库

方法一:改变递归函数的状态转移方程

在原状态转移方程中,每个物品只选取一次导致决策时无论是选还是不选都要将物品下标数+1,下一次对列表中下一个物品做决策。由于在完全背包中物品可以随意取用,我们如果选择某物品,不必将物品下标+1,因为下一次还可能再选。修改部分如下:

refuse = dfs(index+1, vol_l);
if(vol_l >= goods[index].volume)
	choose = dfs(index, vol_l-goods[index].volume)+goods[index].worth;

方法二:修改物品列表

如果我们在读取物品时,在列表中循环插入K次(k*goods[i].volume < V)物品,其中第 j 个物品(1<j<=k)的体积和价值分别是原来的 j 倍,那么就可以将问题转换为拥有新物品列表的01背包问题,直接沿用原来01背包的递归函数即可。如果进一步采用二进制优化,新物品列表中只需增加1,2,4,8,...,2^n倍体积/价值的物品,这些物品选或不选的组合一共可以排列出选择1到2^(n+1)-1次物品的情况。当然在选用次数并非2^(n+1)-1次时会出现越界的情况,也不用考虑,因为确定的容积大小可以将越界情况排除。

#include <algorithm>

using namespace std;

struct goods
{
    int volume;
    int worth;
};

int N, V;
int state[10001][1010];
goods lst[10000];

int dfs(int code, int vol_l)
{
    int jump, stay = 0;
    if(state[code][vol_l] != -1)
        return state[code][vol_l];


    if(vol_l < 0 || code == N)
        state[code][vol_l] = 0;
    else
    {
        jump = dfs(code+1, vol_l);
        if(vol_l >= lst[code].volume)
            stay = dfs(code, vol_l-lst[code].volume)+lst[code].worth;
        
        state[code][vol_l] = max(jump, stay);
    }   
    return state[code][vol_l];    
}

int main()
{
    for(int i = 0; i < 10001; i++)
        for(int j = 0; j < 1010; j++)
            state[i][j] = -1;

    scanf("%d %d", &N, &V);

    for(int i = 0; i < N; i++)
    {
        scanf("%d %d", &lst[i].volume, &lst[i].worth);
        int add_i = 0;
        for(int j = 1; 2*lst[i+j-1].volume <= V; j++)
        {
            lst[i+j].volume = lst[i+j-1].volume*2;
            lst[i+j].worth = lst[i+j-1].worth*2;
            add_i++;
        }
        i += add_i;
        N += add_i;
    }    
    
    printf("%d", dfs(0, V));
    return 0;
}

发表评论:

Powered By Z-BlogPHP 1.7.3

© 2018-2020 有趣的地方 粤ICP备18140861号-1 网站地图