Showing posts with label DP - Knapsack. Show all posts
Showing posts with label DP - Knapsack. Show all posts

DP - Knapsack Summary


X. https://blog.csdn.net/u010982765/article/details/79044613
01背包问题内层循环必须是逆序的解释
有N件物品和一个容量为V的背包。第i件物品的重量是c[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

(01背包中这些物品每种都只有1个,每个物品只能装一次)

基本问题

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
则其状态转移方程便是:f[i][v]=max{ f[i-1][v], f[i-1][v-c[i]]+w[i] }。可以压缩空间,f[v]=max{f[v],f[v-c[i]]+w[i]}

这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。
如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,
那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,
此时能获得的最大价值就是f [i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i] 即f[i-1][v-c[i]]+w[i]。
为什么逆序

逆序的关键就在于这个状态转移方程

f[i][v]只与f[i-1][v]和f[i-1][v-C[i]]有关,即只和i-1时刻状态有关,所以我们只需要用一维数组f[]来保存i-1时的状态f[]。
假设i-1时刻的f[]为{a0,a1,a2,…,av},那么i时刻的f[]中第v个应该为max(av,av-C[i]+W[i])即max(f[v],f[v-C[i]]+W[i]),

这就需要我们遍历V时逆序遍历,这样才能保证求i时刻f[v]时f[v-C[i]]是i-1时刻的值。如果正序遍历则当求f[v]时
其前面的f[0],f[1],…,f[v-1]都已经改变过,里面存的都不是i-1时刻的值,这样求f[v]时利用f[v-C[i]]必定是错的值。最后f[V]即为最大价值。

数组遍历有时候必须逆序大多是这个原因
https://www.jiuzhang.com/qa/4313/
我没有想明白,为什么有重复的时候i是从coin开始算到s,而没有重复的时候i是从s开始算到coin?
/** 
 * @return number of ways to make sum s using repeated coins
 */
public static int coinrep(int[] coins, int s) {
    int[] dp = new int[s + 1]; 
    dp[0] = 1;          
    for (int coin : coins)      
        for (int i = coin; i <= s; i++)         
            dp[i] += dp[i - coin];                                  
    return dp[s];
}                                       
                                            
/**
 * @return number of ways to make sum s using non-repeated coins
 */
public static int coinnonrep(int[] coins, int s) {
    int[] dp = new int[s + 1];
    dp[0] = 1;  
    for (int coin : coins)
        for (int i = s; i >= coin; i--)
            dp[i] += dp[i - coin];              
    return dp[s];                                                   
}
无重复背包即01背包,得到的结果dp[i]是根据之前的结果来的,换句话说,是否选入当前物品是根据之前没有当前物品的子结果而做出的选择,是为了保证每一个物品的唯一性。
有重复背包即完全背包,每个物品都有无限的可重复性,所以当前的结果要从之前已经出现过当前物品的子结果中得到。
所以循环的方向要反一下,一个从下到上,一个从上到下。

X. 完全背包的顺序
https://blog.csdn.net/bujuan827/article/details/52176137
在这种情况下只能采用方式一,把对n的历遍放到第一层循环,这样才能避免把[1,5]、[5,1]算作两条路径。因为你限制了1,5的顺序,

到了i=5之后不可能在发生5,1的情况产生。

对于方式二,把对n的历遍放在第二层,对于任意的一个状态v,都可能历遍每一种硬币,会导致重复冗余的问题。

如果想加深理解,建议最好把两种方式都实现一下,单步执行查看
https://www.jiuzhang.com/qa/2863/
首先2维的状态是一个完整的状态,能表示状态详细的信息,为什么会有变成1维,实际上是在2维的前提下,优化了空间复杂度。
本质来说, f[i][*] 只和 f[i - 1][*] 有关,这给我了我们优化空间复杂度的契机,所有可以优化到一维。(又因为物品是取一个还是可以无限取,那么在一维状态的时候就要仔细考虑如何枚举状态的顺序)。
接下来是关于状态顺序的枚举。就是顺序是怎么思考的。
动态规划问题枚举的顺序只有一个原则如果b状态的计算依赖于a状态,那么a状态必须在b状态之前被枚举和计算
k sum 问题也是如此。仔细看转移方程:
f[i][j][t] = f[i - 1][j - 1][t - A[i - 1]];
所以i需要从小到大枚举,j和t 也是如此。比如这里 你交换i和j的顺序,并没有剖坏我们的原则,则也是正确的,如下:
    public int  kSum(int A[], int k, int target) {
        int n = A.length;
        int[][][] f = new int[n + 1][k + 1][target + 1];
        for (int i = 0; i < n + 1; i++) {
            f[i][0][0] = 1;
        }
        for (int j = 1; j <= k; j++) {
             for (int i = j; i <= n; i++) {
                for (int t = 1; t <= target; t++) {
                    f[i][j][t] = 0;
                    if (t >= A[i - 1]) {
                        f[i][j][t] = f[i - 1][j - 1][t - A[i - 1]];
                    }
                    f[i][j][t] += f[i - 1][j][t];
                } // for t
            } // for j
        } // for i
        return f[n][k][target];
    }
https://www.jiuzhang.com/qa/6568/
不同的顺序算一种方案,先for物品,再for容量,表示前i个物品用了容量j的价值。这样就没有考虑到顺序对答案的影响。
不同的顺序算多种方案,先for容量,再for物品,表示前j的容量,用了前i个物品的价值,这样就相当于当我用了j的容量,前面1->i-1的物品顺序已经确定了,方案数为dp[j],那么再进行转移,这样就考虑了物品的顺序。
以后遇到这样的问题,要仔细分析一下状态的转移方程,哪些该转移,哪些不该转移,分析清楚了,做法自然就出来了。

Consumer - HDU 3449


http://acm.hdu.edu.cn/showproblem.php?pid=3449
FJ is going to do some shopping, and before that, he needs some boxes to carry the different kinds of stuff he is going to buy. Each box is assigned to carry some specific kinds of stuff (that is to say, if he is going to buy one of these stuff, he has to buy the box beforehand). Each kind of stuff has its own value. Now FJ only has an amount of W dollars for shopping, he intends to get the highest value with the money.

Input
The first line will contain two integers, n (the number of boxes 1 <= n <= 50), w (the amount of money FJ has, 1 <= w <= 100000) Then n lines follow. Each line contains the following number pi (the price of the ith box 1<=pi<=1000), mi (1<=mi<=10 the number goods ith box can carry), and mi pairs of numbers, the price cj (1<=cj<=100), the value vj(1<=vj<=1000000)

Output
For each test case, output the maximum value FJ can get

Sample Input
3 800 300 2 30 50 25 80 600 1 50 130 400 3 40 70 30 40 35 60

Sample Output
210
https://www.cnblogs.com/wangmengmeng/p/4840840.html
题意:有n件物品,对应有不同的价格和价值,这是典型的01背包。但现在有了一个限制,要买物品先买能装这件物品的特定的盒子,盒子的价值为0
代码理解得还不是太好,感觉这是一个“二重”的01背包。首先假设先买第i个盒子,对每个盒子里的物品进行一次01背包;然后对盒子再进行一次01背包,决策到底要不要买这个盒子
dp[i][j]表示前i个盒子有j元钱能获得的最大价值,则所求就是dp[n][total]
因为物品对盒子有了“依赖”,所以要先对dp赋值为-1,表示买不到盒子就更不可能装物品
16         memset(dp,-1,sizeof(dp)); //有依赖关系,要赋初值-1
17         memset(dp[0],0,sizeof(dp[0]));
18         for(int i=1;i<=n;i++){
19             int box,m;
20             scanf("%d%d",&box,&m);
21             for(int j=box;j<=total;j++)
22                 dp[i][j]=dp[i-1][j-box];//先让i层买盒子,因为盒子没有价值,
23                                         //所以直接等于上一层的花费+盒子钱
24             for(int j=0;j<m;j++){//在已花费盒子钱的基础上,此时再对dp[i]层做01背包,
25                                  //即i层一个盒子多种物品的最大价值    
26                 int c,w;
27                 scanf("%d%d",&c,&w);
28                 for(int k=total;k>=c;k--){
29                     if(dp[i][k-c]!=-1)//注意依赖背包有不可能的情况,这里即k买不到盒子和这个物品,
30                                       //不能装物品
31                         dp[i][k]=max(dp[i][k],dp[i][k-c]+w);// 这里不能dp[i][k]=max(dp[i][j],dp[i][k-box-c]+w) 
32                                                             //因为已经买过盒子了,这个表达式代表一个盒子基础上一个物品带一个盒子
33                 }
34             }
35             for(int j=0;j<=total;j++)//决策是否买第i个盒子
36                 dp[i][j]=max(dp[i][j],dp[i-1][j]);//不要忘了考虑不选当前组的情况(不是必选)
37         }
https://www.cnblogs.com/fzl194/p/8833098.html
所以先对每一个箱子进行01背包,保存可以凑出的所有的花费和该花费的最大价值,这是一组中的所有状态,且只能取一个或者不取,背包转化成分组背包,然后就可以做了。
 9 const int maxn = 1e5 + 10;
10 int T, n, m, cases;
11 struct node
12 {
13     int price;
14     int num;
15     int price_sum;
16     int cost[11], value[11];
17     int dp[1005];
18 };
19 node a[100];
20 int dp[maxn];
21 int main()
22 {
23     while(cin >> n >> m)
24     {
25         memset(dp, 0, sizeof(dp));
26         memset(a, 0, sizeof(a));
27         for(int i = 0; i < n; i++)
28     {
29         scanf("%d%d", &a[i].price, &a[i].num);
30         a[i].price_sum = 0;
31         for(int j = 0; j < a[i].num; j++)
32         {
33             scanf("%d%d", &a[i].cost[j], &a[i].value[j]);
34             a[i].price_sum += a[i].cost[j];
35         }
36     }
37     for(int i = 0; i < n; i++)//对,每个箱子预处理出所有可凑出的花费和该花费的最大价值
38     {
39         memset(a[i].dp, -1, sizeof(a[i].dp));
40         a[i].dp[0] = 0;
41         for(int j = 0; j < a[i].num; j++)
42         {
43             for(int k = a[i].price_sum; k >= a[i].cost[j]; k--)
44                 if(a[i].dp[k - a[i].cost[j]] >= 0)a[i].dp[k] = max(a[i].dp[k], a[i].dp[k - a[i].cost[j]] + a[i].value[j]);
45         }/*
46         for(int j = 0; j <= a[i].price_sum; j++)
47             cout<<a[i].dp[j]<<" ";
48         cout<<endl;*/
49     }
50     for(int i = 0; i < n; i++)//枚举每一个的箱子
51     {
52         vector<Pair>d;
53         for(int j = 0; j <= a[i].price_sum; j++)//将该箱子的所有状态存下来
54         {
55             if(a[i].dp[j] > 0)
56                 d.push_back(Pair(j + a[i].price, a[i].dp[j]));
57         }
58         for(int v = m; v >= 0; v--)//枚举花费
59         {
60             for(int j = 0; j < d.size(); j++)//枚举改组的状态
61                 if(v >= d[j].first)
62                     dp[v] = max(dp[v], dp[v - d[j].first] + d[j].second);
63         }
64     }
65     cout<<dp[m]<<endl;
66     }
还有一种写法,在dp的时候把预处理和状态转化合并起来,时间复杂度降低了一点
10 int T, n, m, cases;
11 int a[105];
12 int dp[55][100005];
13 struct node
14 {
15     int v, w;
16 };
17 vector<node>G[105];
18 int main()
19 {
20     while(cin >> n >> m)
21     {
22         memset(dp, 0, sizeof(dp));
23         for(int i = 1; i <= n; i++)G[i].clear();
24         int tot, x, y;
25         for(int i = 1; i <= n; i++)
26         {
27             scanf("%d%d", &a[i], &tot);
28             for(int j = 0; j < tot; j++)
29             {
30                 scanf("%d%d", &x, &y);
31                 G[i].push_back(node{x, y});
32             }
33         }
34 
35         for(int i = 1; i <= n; i++)//枚举每种箱子
36         {
37             for(int j = 0; j < a[i]; j++)dp[i][j] = -1;
38             for(int j = a[i]; j <= m; j++)dp[i][j] = dp[i - 1][j - a[i]];//这里是确保先购买购物车
39 
40             for(int j = 0; j < G[i].size(); j++)//在购物车内进行01背包
41             {
42                 for(int k = m; k >= G[i][j].v; k--)
43                 {
44                     if(dp[i][k - G[i][j].v] != -1)
45                         dp[i][k] = max(dp[i][k], dp[i][k - G[i][j].v] + G[i][j].w);
46                 }
47             }
48             for(int j = 0; j <= m; j++)dp[i][j] = max(dp[i - 1][j], dp[i][j]);//和之前的值比较
49         }
50         cout<<dp[n][m]<<endl;
51     }


金明的预算方案


https://vijos.org/p/1313
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)请你帮助金明设计一个满足要求的购物单。

格式

输入格式

输入文件的第1行,为两个正整数,用一个空格隔开:
N m 
其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)
从第2行到第m\+1行,第j行给出了编号为j\-1的物品的基本数据,每行有3个非负整数
v p q
(其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)

输出格式

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值
(<200000)。

样例1

样例输入1

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

样例输出1

2200

限制

1s
考虑到每个主件最多只有两个附件,因此我们可以通过转化,把原问题转化为01背包问题来解决,在用01背包之前我们需要对输入数据进行处理,把每一种物品归类,即:把每一个主件和它的附件看作一类物品。处理好之后,我们就可以使用01背包算法了。在取某件物品时,我们只需要从以下四种方案中取最大的那种方案:只取主件、取主件+附件1、取主件+附件2、既主件+附件1+附件2。很容易得到如下状态转移方程:

f[i,j]=max{f[i-1,j],

f[i-1,j-a[i,0]]+a[i,0]*b[i,0],

f[i-1,j-a[i,0]-a[i,1]]+a[i,0]*b[i,0]+a[i,1]*b[i,1],

f[i-1,j-a[i,0]-a[i,2]]+a[i,0]*b[i,0]+a[i,2]*b[i,2],

f[i-1,j-a[i,0]-a[i,1]-a[i,2]]+a[i,0]*b[i,0]+a[i,1]*b[i,1]+a[i,2]*b[i,2]}

其中,f[i,j]表示用j元钱,买前i类物品,所得的最大值,a[i,0]表示第i类物品主件的价格,a[i,1]表示第i类物品第1个附件的价格,a[i,2]表示第i类物品第2个附件的价格,b[i,0],b[i,1],b[i,2]分别表示主件、第1个附件和第2个附件的重要度。

for(i=1;i<=m;i++)
{
cin>>c>>p>>q;
c/=10; //同上 
if(q==0) {w[i][q]=c; v[i][q]=c*p;}
else if(w[q][1]==0) {w[q][1]=c;v[q][1]=c*p;}
else {w[q][2]=c;v[q][2]=c*p;}
}
for(i=1;i<=m;i++)
for(j=0;j<=n;j++)
{
d[i][j]=d[i-1][j];
if(j>=w[i][0]) {t=d[i-1][j-w[i][0]]+v[i][0];if(t>d[i][j]) d[i][j]=t;}
if(j>=w[i][0]+w[i][1]) {t=d[i-1][j-w[i][0]-w[i][1]]+v[i][0]+v[i][1];if(t>d[i][j]) d[i][j]=t;}
if(j>=w[i][0]+w[i][2]) {t=d[i-1][j-w[i][0]-w[i][2]]+v[i][0]+v[i][2];if(t>d[i][j]) d[i][j]=t;}
if(j>=w[i][0]+w[i][1]+w[i][2]) {t=d[i-1][j-w[i][0]-w[i][1]-w[i][2]]+v[i][0]+v[i][1]+v[i][2];if(t>d[i][j]) d[i][j]=t;}
}
好吧就是一维四状态的背包
    scanf("%d%d",&n,&m);
    /*q,v:如是主件则存在这里
    q1,v1:如是附件一存在这里
    q2,v2:如是附件二则存在这里*/
    memset(f,-1,sizeof(f));
    memset(q,0,sizeof(q));
    memset(q1,0,sizeof(q1));
    memset(q2,0,sizeof(q2));
    memset(v,0,sizeof(v));
    memset(v1,0,sizeof(v1));
    memset(v2,0,sizeof(v2));
    //请自动忽略以上的的OVO

    f[0]=0;
    for (int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if (c==0)
        {
            v[i]=a;q[i]=b;  //存为主件  
        }    
        else 
        {
            if (q1[c]==0) {q1[c]=b;v1[c]=a;}
            else {q2[c]=b;v2[c]=a;}
            //存为附件一或附件二
        }
    }    
    for (int i=1;i<=m;i++)
    {
        for (int j=n;j>=v[i];j--)
        {
            //if(f[j]!=-1)
            {
                if (j-v[i]>=0) f[j]=mymax(f[j],f[j-v[i]]+v[i]*q[i]);//只买一个主件
                if (j-v[i]-v1[i]>=0) f[j]=mymax(f[j],f[j-v[i]-v1[i]]+v[i]*q[i]+v1[i]*q1[i]);//买主件和附件一
                if (j-v[i]-v2[i]>=0) f[j]=mymax(f[j],f[j-v[i]-v2[i]]+v[i]*q[i]+v2[i]*q2[i]);//买主件和附件二
                if (j-v[i]-v1[i]-v2[i]>=0) f[j]=mymax(f[j],f[j-v[i]-v1[i]-v2[i]]+v[i]*q[i]+v1[i]*q1[i]+v2[i]*q2[i]);//买主件和两个附件
            }
        }
    }
    int ans=0;
    for (int j=1;j<=n;j++)
    {
        if (f[j]>ans) ans=f[j];
        //不一定用最多钱的就是最优的,扫一遍最大值
    }

方便起见,我们把状态只定义在主件上(对输入进行特殊处理) 
每个主件有0,1,2个附件 
每次购买时,有五种选择 
1.不买 
2.只买主件 
3.买主件和一号附件 
4.买主件和二号附件 
5.买主件和全部附件

[i][0]表示主件的价值与花费 
[i][1]表示一号附件的价值与花费 
[i][2]表示二号附件的价值与花费 
当一个物品没有附件时,[i][1]和[i][2]均为0

状态转移方程(只是一个思路方程,不完整!没有写乘上期望值什么的,具体方程请往下看代码) 
f(i,j) = max ( 
f(i-1,j),//不买 
f(i-1,j - c[i][0]) + v[i][0],//只买主件 
f(i-1,j - c[i][0] - c[i][1]) + v[i][0] + v[i][1],//主件和一号附件 
f(i-1,j - c[i][0] - c[i][2]) + v[i][0] + v[i][2],//主件和二号附件 
f(i-1,j - c[i][0] - c[i][1] - c[i][2]) + v[i][0] + v[i][1] + v[i][2],//主件和全部附件 
)

    for(int i=1; i<=m; i++) {
        int w, p, q;
        scanf("%d %d %d",&w,&p,&q);
        if(q == 0) {
            c[i][0] = w;
            v[i][0] = p;
            continue;

        }
        if(!v[q][1]) { // 如果第一个附件还没有遇到 
            c[q][1] = w;
            v[q][1] = p;
        }
        else { //如果已经遇到第一个附件 
            c[q][2] = w;
            v[q][2] = p;
        }
    }
    for(int i=1; i<=m; i++) {

        for(int j=10; j<=n; j+=10){

            if(j-c[i][0]>= 0) {

                f[i][j] = std::max(f[i-1][j],f[i-1][j-c[i][0]] + v[i][0]*c[i][0]);

            if(j - c[i][0] - c[i][1] >= 0)

                f[i][j] = std::max(f[i][j],f[i-1][j-c[i][0]-c[i][1]] + v[i][1]*c[i][1] + v[i][0]*c[i][0]);  

            if(j - c[i][0] - c[i][2]>= 0)

                f[i][j] = std::max(f[i][j],f[i-1][j-c[i][0]-c[i][2]] + v[i][2]*c[i][2] + v[i][0]*c[i][0]);

            if(j-c[i][0] - c[i][1] - c[i][2] >= 0)

                f[i][j] = std::max(f[i][j],f[i-1][j-c[i][0]-c[i][1]-c[i][2]] + v[i][2]*c[i][2] + v[i][1]*c[i][1] + v[i][0]*c[i][0]);

            }
            else
                f[i][j] = f[i-1][j];
        }
    }
    printf("%d",f[m][n]);


HDU 1712 - ACboy needs your help


http://acm.hdu.edu.cn/showproblem.php?pid=1712
ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?

Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.
Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].
N = 0 and M = 0 ends the input.

Output
For each data set, your program should output a line which contains the number of the max profit ACboy will gain.

Sample Input
2 2 1 2 1 3 2 2 2 1 2 1 2 3 3 2 1 3 2 1 0 0

Sample Output
3 4 6
http://www.cppblog.com/Onway/articles/122695.html
可以将每一门课看成一个分组,每门课不同天数的选择看成是分组的物品(显然只能有一个选择),物品的费用即为花费的天数,物品的价值为题中给出的收获。该题中背包容量最大为M。
设dp[x]为前i组物品,在背包容量为x(即费用为x)时的最大价值。则将i从1到N进行过历遍后(第一重循环),dp[m]即为所求。
在这种状态设置中,容易想出以下两种阶段递推方式(以下所述都为第二和第三重循环):
1,在同一个背包容量中,对不同费用的物品进行枚举比较:
for(j=MAX;j>=1;--j)   //背包容量
      for(k=1;k<=m;++k)  //不同费用的物品
2,在同一费用的物品中,对放在不同背包容量时计算最大价值:(该方式同《背包九讲-分组背包》中的伪代码部分)
for(k=1;k<=m;++k)    //不同费用的物品


      for(j=MAX;j>=1;--j)  //背包容量
1,分析第一种递推方式的正确:
该方式即求在容量为j的背包中,选择哪一个物品可以有最大价值。
看递推方程:dp[j]=max(dp[j],dp[j-c[k]]+w[k]);(其中c[k]为k物品的费用,w[k]为价值),由于递降枚举背包容量,max比较中的dp[j]是由上一组物品决策所得,在这里将被忽略。因为就算不忽略,在本组物品中dp[j]的决策依然要取决于dp[j-c[k]]+w[k]。
而同样由于递降枚举背包容量(第二重循环),dp[j-c[k]]在本组物品中是未进行过决策的,亦即背包容量为j-c[k]时,在本组物品中是没有选择任何物品的,这可以保证对dp[j]决策时,不会多选本组中的物品。
2,分析第二种递推方式的错误:
该方式即求对物品k,放在所有背包中,计算各个最大价值。
同样是递推方程:dp[j]=max(dp[j],dp[j-c[k]]+w[k]);(其中c[k]为k物品的费用,w[k]为价值)。能否保证dp[j-c[k]]在本组中未经决策,就成了该递推方式对错的关键。
由于背包容量的递降枚举在第三重循环,只能保证k物品不会重复选择。对于另一k0物品,当背包容量枚举到j-c[k]的时候,由方程可以有:dp[j-c[k]]=max(dp[j-c[k]],dp[j-c[k]-c[k0]]+w[k0],亦即dp[j-c[k]]可能在本组中的其他物品中进行过决策。


那么这样就可能导致在一组物品中选择了多件物品。
https://blog.csdn.net/loveyou11111111/article/details/50674854
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选 。 也就是说设 f[k][v]表示前 k 组物品花费费用 v 能取得的最大权值,则有:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品 i 属于组 k}
使用一维数组的伪代码如下:
for 所有的组 k
for v=V..0
for 所有的 i 属于组 k
f[v]=max{f[v],f[v-c[i]]+w[i]}

        for(int i=1;i<=n;i++)
        {
            for(int j=m;j>=1;j--)
            {
                for(int k=1;k<=m;k++)
                    if(j-k>=0)
                        dp[i][j]=max(max(dp[i][j],dp[i-1][j]),dp[i-1][j-k]+a[i][k]); //一维就可以,很好改,我就不改了
            }
        }
        printf("%d\n",dp[n][m]);
    }
https://blog.csdn.net/u013480600/article/details/40649797
       所以我们换一种问题描述方式: 有n组物品, 每组物品有m个且每组物品中最多只能选1个物品. 第i组物品的花费分别为1 2 3 …m, 第i组物品的价值分别为val[i][1], val[i][2]…val[i][m]. 现在问你初始金钱为m时, 通过上面n组物品, 最多能获得多少价值的物品?

       上面问题就是一个明显的分组背包问题了. 我们令dp[i][j]==x表示只选前i组物品且总花费<=j时, 能获得的最大价值为x.

       初始化: dp全为0.

       状态转移: dp[i][j] == max( dp[i-1][j] , dp[i-1][j-cost[k]]+val[k])

       其中cost[k]和val[k]指的是第i组物品的第k个物品的花费和价值.

       上面公式前者表示第i组物品一个都不选, 后者表示第i组物品选1个.

       最终所求: dp[n][m]的值.

注意: dp递推的3层循环的相互顺序不能改变, 否者会错.(可以自己想一想为什么是这样的循环顺序).程序用的滚动数组, dp只有一维.


    while(scanf("%d%d",&n,&m)==2 && n)
    {
        //读取输入
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&val[i][j]);
            cost[i][j]=j;
        }

        //递推过程
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)//第i组
        for(int j=m;j>=0;j--)//<=j花费时
        for(int k=1;k<=m;k++)//第i组的第k个物品
        {
            if(j>=cost[i][k])
                dp[j] = max(dp[j], dp[j-cost[i][k]]+val[i][k]);
        }

https://www.cnblogs.com/00isok/p/9379331.html
这是一个很明显的分组背包问题,将某一门课程花m个不同天数能够得到不同的价值看成是m个有各自花费和价值的物品,然后,又因为根据题意,每一门课程都只能选择一种花费的天数,于是,这道题就被很自然的转化为分组背包问题。
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++) {
                scanf("%d", &arr[i][j]);
                c[i][j] = j;     //记录要花费的天数( 即背包中的体积 )
            }
        }

        int dp[110];
        memset(dp, 0, sizeof(dp));

        for (int k = 1; k <= n; k++)             //第k门课程       (对应分组背包中的组序号)
        {
            for (int v = m; v >= 0; v--)         //总共所花的天数  (对应背包的容量)由于此题每组物品只能取一次,所以逆序
            {
                for (int i = 1; i <= m; i++)     //第k门课中序号为i的物品    
                {
                    if (v - i < 0)continue;
                    dp[v] = max(dp[v], dp[v - c[k][i]] + arr[k][i]);
                }
            }
        }      //dp[i][j]为,前i组中花费天数为v时,所能得到的最大价值

https://blog.csdn.net/u012860063/article/details/34107953
题意:一开始输入n和m,n代表有n门课,m代表你有m天,
然后给你一个数组,val[i][j],代表第i门课,在通过j天去修,
会得到的分数。求在m天能得到的最大分数。
while(~scanf("%d%d",&n,&m)&&( n || m))
{
memset(dp,0,sizeof(dp));
for(i = 1; i <=n ; i++)
{
for(j = 1; j <= m ;j++)
scanf("%d",&a[i][j]);
}
for(i = 1 ; i <= n ; i++)//第一重循环:分组数
{
for(j = m ; j >= 1 ; j--) //第二重循环:容量体积
{
for(k = 1 ; k <= j ; k++) //第三重循环:属于i组的k
{
dp[j]=max(dp[j],dp[j-k]+a[i][k]);
}
}
}
printf("%d\n",dp[m]);
}

http://blog.sina.com.cn/s/blog_79b832820100rfpc.html
    for (i=0;i<n;i++)
        for (j=m;j>=1;j--)     //第二重循环和第三重循环不能交换,否则变成了一个课程可以用不同天多次完成。
            for (k=1;k<=j;k++)
                f[j]=max(f[j],f[j-k]+v[i][k]);

    return f[m];


Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts