动态规划扩展:背包小结

01背包是基础的背包问题,即容量为v的背包, 给你n件物品, 每件物品只有一件, 每件物品所占体积vi, 价值wi已知,求此背包所能容纳的前提下,让在其中物品价值最大。

此问题状态方程为发f[i][v] = max(f[i-1][v], f[i-1][v-vi[i]]+wi[i]), 即前i件物品放入容积为v的背包中得到的最大价值。

因为每件物品只有一件,所以第i件物品有两种选择, 即放或不放。

max(f[i-1][v], f[i-1][v-vi[i]]+wi[i])中,f[i-1][v]即选择不放, 价值保持放入i-1 个物品时的价值;
而f[i-1][v-vi[i]]+wi[i]即选择将第i件物品放入, 从其中选择较大的值就是即为前i件物品放入容积为v的背包得到的最大价值,最后f[n][v]就是要求的价值最大值;

code:

for(i = 0;i<n;i++)
        {
            for(j = vi[i];j<=v;j++)
            {
                f[i][j] = max(f[i-1][j], f[i-1][j-vi[i]]+wi[i]);
            }
        }

另外, 还可用一维数组代替二维数组的方法实现空间优化,即用最新的一组数据代替原先的上一组数据,不过此方法写循环式需要逆序循环, 即二层循环从v开始一直到vi[i],这样就保证了每个物品只拿一次,而顺序则不能保证,可能拿多次。
于是f[v] = max(f[v], f[v-vi]+wi[i], 最后的f[v]即为要求的最大价值。

code:

for(i = 0;i<n;i++)
        {
            for(j = v;j>=vi[i];j--)
            {
                f[j] = max(f[j], f[j-vi[i]]+wi[i]);
            }
        }

例题1:
Problem Description

多组输入。

对于每组输入,第一行有两个整数N,X(1 < = N < = 100,1 < = X < = 1000),分别表示哈士奇的数量和高数巨的钱数

对于每组数据,输出一个整数,表示高数巨最多可以获得的萌值,每组输出占一行

Example Input

2 100
50 20
60 40
3 100
20 55
20 35
90 95
1 10
20 50
Example Output

40
95
0
明显的01背包问题,这里所带的钱就相当于背包的体积,萌值就是价值,每条二哈的价钱可看做每个物品所占的体积。

code:

#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
    if(a>b) return a;
    else return b;
}
int main()
{
    int b[1200];
    int n, x, pi[1200], mi[1200], i, j, a;
    while(~scanf("%d%d", &n, &x))
    {
        memset(b, 0, sizeof(b));
        for(i = 0;i<n;i++)
        {
            scanf("%d%d", &pi[i], &mi[i]);
        }
        for(i = 0;i<n;i++)
        {
            for(j = x;j>=pi[i];j--)
            {
                b[j] = max(b[j], b[j-pi[i]]+mi[i]);
            }
        }
        printf("%d\n", b[v]);
    }
}


有了01背包基础,接下来的完全背包聚会简单很多,完全背包实际上就是将每种物品的数量从一件变为无限件。那么设某i件物品拿k件,那么状态方程为

f[i][v]=max(f[i-1][v-k*v[i]]+k*w[i]|0<=k*v[i]<=v),这样需要三层循环,找出k为何值时使得f得到最大值;

另外完全背包也可以使用一维数组进行空间优化, 可以用01背包提到过的正序循环,这样就可以保证每件物品拿多次。

01背包中要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1] [v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的 子结果f[i-1][v-v[i]]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-v[i]],所以就可以并且必须采用v=0..V的顺序循环。(本段节录自背包九讲)
code:

for(i = 0;i<n;i++)
        {
            for(j = vi[i];j<=v;j++)
            {
                f[j] = max(f[j], f[j-vi[i]]+wi[i]);
            }
        }

例题1:

Problem Description

话说,上次小P到伊利哇呀国旅行得到了一批宝藏。他是相当开心啊,回来就告诉了他的好基友小鑫,于是他们又结伴去伊利哇呀国寻宝。
这次小P的寻宝之路可没有那么的轻松,他们走到了一个森林,小鑫一不小心被触发了机关,被困在了一个大笼子里面,笼子旁边上有一道题目和一个密码锁,上面说只要解出此题输入密码即可救出被困人。小鑫不是很聪明,所以他做不出来,他知道小P很笨,更解不出来。所以他就让小P独自回去,不用管他。但是小P重情重义不会抛弃他离去。他说:“不,好基友一起走!”。于是就感动了上帝,上帝特派你来替他们解决问题。聪明的你要加油了啊!
题目描述:给你n种物品和一个体积为v的包包。每种物品有无数种,体积是vi价值是wi。求出包包v所能装的最大价值的东西。
Input

多组输入。第一行有两个正整数n(0<n<=10000), v(0<v<= 10000)。接下来两行每行有n个数字。第一行表示每种物品的价值wi(0<wi<100),第二行表示每种物品的体积vi(0<vi<100)。
Output

输出最多可以得到的价值。输出结果救出小鑫。
Example Input

5 20
1 2 3 4 5
2 6 3 5 4
Example Output

25
code:

#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
    if(a>b) return a;
    else return b;
}
int main()
{
    int n, v, vi[10010], wi[10010], a[10010];
    int i, j;
    while(~scanf("%d%d", &n, &v))
    {
        memset(a, 0, sizeof(a));
        for(i = 0;i<n;i++)
        {
            scanf("%d", &wi[i]);
        }
        for(i = 0;i<n;i++)
        {
            scanf("%d", &vi[i]);
        }
        for(i = 0;i<n;i++)
        {
            for(j = vi[i];j<=v;j++)
            {
                a[j] = max(a[j], a[j-vi[i]]+wi[i]);
            }
        }
        printf("%d\n", a[v]);
    }
}

————————————————————————————————————————————————————————

小P的故事——神奇的Dota

Time Limit: 1000 ms Memory Limit: 65536 KiB

Submit Statistic

Problem Description

小P非常喜欢玩dota,不分昼夜的玩

,结果他连做梦也都是里面的画面,一天晚上小P刚躺下就做了一个神奇的梦。。。

不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前.

死亡骑士:"我要买道具!"

地精商人:"我们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个."

死亡骑士:"好的,给我一个血瓶."

说完他掏出那张N元的大钞递给地精商人.

地精商人:"我忘了提醒你了,我们这里没有找客人钱的习惯的,多的钱我们都当小费收了的,嘿嘿."

死亡骑士:"......"

死亡骑士想,与其把钱当小费送个他还不如自己多买一点道具,反正以后都要买的,早点买了放在家里也好,但是要尽量少让他赚小费.

现在死亡骑士感觉自己的智商不够用所以希望小P帮他计算一下,最少他要给地精商人多少小费.但是小P的智商可是出了名的“不忍直视”啊,聪明非凡的你所以你能帮帮他吗?

Input

输入数据的第一行是一个整数T(1<=T<=100),代表测试数据的数量.然后是T行测试数据,每个测试数据只包含一个正整数N(1<=N<=10000),N代表死亡骑士手中钞票的面值.

 

注意:地精商店只有题中描述的三种道具.

Output

 对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人作为小费.

 

Sample Input

2 
380 
200

Sample Output

30 
0

Hint

 

Source

xfl

这也是一个可以那多次的背包问题,所以内循环要用正序;

code:

#include<bits/stdc++.h>
using namespace std;

int main()
{

    int dp[10010],w[3]={150,200,350};
    int i,j,n,t;
    cin>>t;
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        cin>>n;
        for(i=0;i<=2;i++)
        {
            for(j=w[i];j<=n;j++)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
            }
        }
        cout<<n-dp[n]<<endl;

    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值