今天仔细看了一下背包,学习了他的原理,今天也没做难题就整理一下背包。
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背包问题,这里所带的钱就相当于背包的体积,萌值就是价值,每条二哈的价钱可看做每个物品所占的体积

#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-kv[i]]+kw[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

#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]);
    }
}

加油!!!