hdu3033(分组背包二次dp处理)

I love sneakers!

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2895    Accepted Submission(s): 1185


Problem Description
After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store.

There are several brands of sneakers that Iserlohn wants to collect, such as Air Jordan and Nike Pro. And each brand has released various products. For the reason that Iserlohn is definitely a sneaker-mania, he desires to buy at least one product for each brand.
Although the fixed price of each product has been labeled, Iserlohn sets values for each of them based on his own tendency. With handsome but limited money, he wants to maximize the total value of the shoes he is going to buy. Obviously, as a collector, he won’t buy the same product twice.
Now, Iserlohn needs you to help him find the best solution of his problem, which means to maximize the total value of the products he can buy.
 

Input
Input contains multiple test cases. Each test case begins with three integers 1<=N<=100 representing the total number of products, 1 <= M<= 10000 the money Iserlohn gets, and 1<=K<=10 representing the sneaker brands. The following N lines each represents a product with three positive integers 1<=a<=k, b and c, 0<=b,c<100000, meaning the brand’s number it belongs, the labeled price, and the value of this product. Process to End Of File.
 

Output
For each test case, print an integer which is the maximum total value of the sneakers that Iserlohn purchases. Print "Impossible" if Iserlohn's demands can’t be satisfied.
 

Sample Input
      
      
5 10000 3 1 4 6 2 5 7 3 4 99 1 55 77 2 44 66
 

Sample Output
      
      
255
 



题意:用M元买鞋,每种鞋有编号,价钱,自己喜欢程度三个值,要求所给编号每种至少买一双,使得喜欢程度的和最大。就是个分组背包,加了一个条件限制。搜到题解,看了一天多啊。。。看不懂啊。。。问了好多人一样看不懂啊。。。。今天和大神聊天。。。大神一语点醒我。dp啊,果然就是dp。感慨完了。。。继续正题……
    下面就不管鞋不鞋的问题了,直接按照背包讲,钱是容量,满意程度就是价值。状态定义是这样的dp[i][v]代表前i组里面,每组至少选一个,装在容量为v的背包里面可以得到的最大值。这样从什么都没有dp[0][0~V] = 0的合法状态往外拓展(即往外状态转移),不合法状态赋值-1。如果最后dp[k][V]等于-1那就是这个状态根本达不到,那就是不能满足要求。(注意如果给定的编号不齐全,那就直接输出不可能就好了)。这样说一开始就理解但是,我看不懂代码,同时我伴随着一种疑问也解决不了。这些具体在代码中讲解。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define rep(i,n) for(int i = 0;i < n;i ++)
struct node
{
    int v,val;
}tmp;
int dp[11][10101];
int main()
{
    int n, V, k, cnt;
    while(~scanf("%d%d%d",&n,&V,&k))
    {
        bool hash[11];cnt = 0;//这行变量用于判断所给编号是否齐全
           vector<node>gp[11];//用于储存各个组的背包内容
        memset(hash,0,sizeof(hash));


        rep(i,n)
        {
            int num;
            scanf("%d%d%d",&num,&tmp.v,&tmp.val);
            gp[num].push_back(tmp);
            if(!hash[num])cnt++,hash[num] = true;
        }
        if(cnt != k){
            puts("Impossible");
        }else{


            memset(dp,-1,sizeof(dp));//全部初始化不合法
            memset(dp[0],0,sizeof(dp[0]));//前零组就是什么都没有的时候,,所以各种容量值都为0是合法的
//开始是担心之后放进去的物品由于价值更大而取代了前面某组可能只有一件放入的物品,这样就不符合要求了

//但其实不会这样,因为当前能够拓展出状态的条件就是前i-1组已经都至少每组都放进去了,在这种情况下有空间放下

//当前物品,那么这一新状态也就是合法的状态了。i最小为1,i-1为0,0状态的时候都合法。这样一推就都对了。

//其实这一思路是对于每一个组的物品进行0-1背包,只是多了个条件,它必须在可以被前一组拓展的基础上才可以进行0-1背包的操作,否则当前组的物品是不能进行0-1背包的
            for(int i = 1;i <= k;i ++)//枚举组


              for(int j = 0;j < gp[i].size();j ++)//枚举组内各种物品


                for(int v = V;v >= gp[i][j].v;v --)//0-1背包,有点特别
                {//这两个的顺序是不能交换的,因为第一个if语句代表普通0-1背包
		//注意这个不是把滚动数组省去了的意思,多出的一维是描述前几个组的
                   if(dp[i][v - gp[i][j].v] != -1)
                      dp[i][v] = max(dp[i][v],dp[i][v - gp[i][j].v] + gp[i][j].val);

		//这个就是描述把当前物品放入前面组的做法,之所以不能颠倒,是因为当前物品只能
		//放一次,如果颠倒顺序就会给当前物品有一个在本次(前i组)背包里面新增一个合法状态,
		//如果该物品恰好可以拓展该状态,那么就放了两次了,所以不行。dp[i][0~V]的合法
		//状态除了dp[0][0~V]是赋值得到,都必须是从前一组里面拓展得到的这样才可以保证得到
 		//的一切状态符合要求。
                   if(dp[i - 1][v - gp[i][j].v] != -1)
                      dp[i][v] = max(dp[i][v],dp[i - 1][v - gp[i][j].v] + gp[i][j].val);
                }
                
            if(dp[k][V] != -1)
                printf("%d\n",dp[k][V]);
            else puts("Impossible");
          }
    }
   return 0;  
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: hdu 2829 Lawrence 斜率优化dp 这道题是一道经典的斜率优化dp题目,需要用到单调队列的思想。 题目大意是给定一个序列a,求出一个序列b,使得b[i]表示a[1]~a[i]中的最小值,且满足b[i] = min{b[j] + (i-j)*k},其中k为给定的常数。 我们可以将上式拆开,得到b[i] = min{b[j] - j*k} + i*k,即b[i] = i*k + min{b[j] - j*k},这个式子就是斜率优化dp的形式。 我们可以用单调队列来维护min{b[j] - j*k},具体思路如下: 1. 首先将第一个元素加入队列中。 2. 从第二个元素开始,我们需要将当前元素加入队列中,并且需要维护队列的单调性。 3. 维护单调性的方法是,我们从队列的末尾开始,将队列中所有大于当前元素的元素弹出,直到队列为空或者队列中最后一个元素小于当前元素为止。 4. 弹出元素的同时,我们需要计算它们对应的斜率,即(b[j]-j*k)/(j-i),并将这些斜率与当前元素的斜率比较,如果当前元素的斜率更小,则将当前元素加入队列中。 5. 最后队列中的第一个元素就是min{b[j] - j*k},我们将它加上i*k就得到了b[i]的值。 6. 重复以上步骤直到处理完所有元素。 具体实现可以参考下面的代码: ### 回答2: HDU 2829 Lawrence 斜率优化 DP 是一道经典的斜率优化 DP 题目,其思想是通过维护一个下凸包来优化 DP 算法。下面我们来具体分析一下这道题目。 首先,让我们看一下该题目的描述。题目给定一些木棒,要求我们将这些木棒割成一些给定长度,且要求每种长度的木棒的数量都是一样的,求最小的割枝次数。这是一个典型的背包问题,而且在此基础上还要求每种长度的木棒的数量相同,这就需要我们在状态设计上走一些弯路。 我们来看一下状态的定义。定义 $dp[i][j]$ 表示前 $i$ 个木棒中正好能割出 $j$ 根长度为 $c_i$ 的木棒的最小割枝次数。对于每个 $dp[i][j]$,我们可以分类讨论: 1. 不选当前的木棒,即 $dp[i][j]=dp[i-1][j]$; 2. 选当前的木棒,即 $dp[i][j-k]=dp[i-1][j-k]+k$,其中 $k$ 是 $j/c_i$ 的整数部分。 现在问题再次转化为我们需要在满足等量限制的情况下,求最小的割枝次数。可以看出,这是一个依赖于 $c_i$ 的限制。于是,我们可以通过斜率优化 DP 来解决这个问题。 我们来具体分析一下斜率优化 DP 算法的思路。我们首先来看一下动态规划的状态转移方程 $dp[i][j]=\min\{dp[i-1][k]+x_k(i,j)\}$。可以发现,$dp[i][j]$ 的最小值只与 $dp[i-1][k]$ 和 $x_k(i,j)$ 有关。其中,$x_k(i,j)$ 表示斜率,其值为 $dp[i-1][k]-k\times c_i+j\times c_i$。 接下来,我们需要维护一个下凸包,并通过斜率进行优化。我们具体分析一下该过程。假设我们当前要计算 $dp[i][j]$。首先,我们需要找到当前点 $(i,j)$ 在凸包上的位置,即斜率最小值的位置。然后,我们根据该位置的斜率计算 $dp[i][j]$ 的值。接下来,我们需要将当前点 $(i,j)$ 加入到下凸包上。 我们在加入点的时候需要注意几点。首先,我们需要将凸包中所有斜率比当前点小的点移除,直到该点能够加入到凸包中为止。其次,我们需要判断该点是否能够加入到凸包中。如果不能加入到凸包中,则直接舍弃。最后,我们需要保证凸包中斜率是单调递增的,这就需要在加入新的点之后进行上一步操作。 以上就是该题目的解题思路。需要注意的是,斜率优化 DP 算法并不是万能的,其使用情况需要根据具体的问题情况来确定。同时,该算法中需要维护一个下凸包,可能会增加一些算法的复杂度,建议和常规 DP 算法进行对比,选择最优的算法进行解题。 ### 回答3: 斜率优化DP是一种动态规划优化算法,其主要思路是通过对状态转移方程进行变形,提高算法的时间复杂度。HDU2829 Lawrence问题可以用斜率优化DP解决。 首先,我们需要了解原问题的含义。问题描述如下:有$n$个人在数轴上,第$i$个人的位置为$A_i$,每个人可以携带一定大小的行李,第$i$个人的行李重量为$B_i$,但是每个人只能帮助没有他们重量大的人搬行李。若第$i$个人搬运了第$j$个人的行李,那么第$i$个人会累加$C_{i,j}=\left|A_i-A_j\right|\cdot B_j$的体力消耗。求$m$个人帮助每个人搬运行李的最小体力消耗。 我们可以通过斜率优化DP解决这个问题。记$f_i$为到前$i$个人的最小体力消耗,那么状态转移方程为: $$f_i=\min_{j<i}\{f_j+abs(A_i-A_j)\cdot B_i\}$$ 如果直接使用该方程,时间复杂度为$O(n^2)$,如果$n=10^4$,则需要计算$10^8$次,运算时间极长。斜率优化DP通过一些数学推导将方程变形,将时间复杂度降低到$O(n)$,大大缩短了计算时间。 通过斜率优化DP的推导式子,我们可以得到转移方程为: $$f_i=\min_{j<i}\{f_j+slope(j,i)\}$$ 其中,$slope(j,i)$表示直线$j-i$的斜率。我们可以通过如下方式来求解$slope(j,i)$: $$slope(j,i)=\frac{f_i-f_j}{A_i-A_j}-B_i-B_j$$ 如果$slope(j,i)\leq slope(j,k)$,那么$j$一定不是最优,可以直接舍去,降低计算时间。该算法的时间复杂度为$O(n)$。 综上所述,斜率优化DP是一种动态规划优化算法,可以大大缩短计算时间。在处理类似HDU2829 Lawrence问题的时候,斜率优化DP可以很好地解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值