【01背包的k值问题 HDU2639 HDU2126】

HDU2639
有深度吧感觉还
题意:给出一行价值,一行体积,让你在v体积的范围内找出第k大的值
终结第k大的01背包,
复杂度: O(NMK)
注意选取的标准,选取后,要保证得到的价值是大于未选之前,选之后若是相等的话,这样应该是不能选的,因为占用了空间
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll moad=1e9+7;
const int maxn=1e6+10;
int dp[1005][35];
int w[1005];
int c[1005];
int A[1005];
int B[1005];
int main()
{
    int t;
    int n,m,K;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>K;
        for(int i=0;i<n;++i)cin>>w[i];
        for(int i=0;i<n;++i)cin>>c[i];
        memset(dp,0,sizeof(dp));
        memset(A,0,sizeof(A)),memset(B,0,sizeof(B));
        for(int i=0;i<n;++i)
        {
            for(int j=m;j>=c[i];--j)
            {
                for(int k=1;k<=K;++k)
                {
                    A[k]=dp[j-c[i]][k]+w[i];//yes
                    B[k]=dp[j][k];//no
                }
                int a=1,b=1,c=1;
                while(c<=K&&(a<=K||b<=K))
                {
                    if(A[a]>B[b]) dp[j][c]=A[a],++a;
                    else dp[j][c]=B[b],++b;

                    if(dp[j][c]!=dp[j][c-1])++c;

                }
            }
        }
        cout<<dp[m][K]<<endl;
    }
    return 0;
}

HDU2126
题意:给出n个不同得数字,然后给出一个m,问使得挑选的数字的个数尽量多(这些数字之和小于等于m),输出最多挑选的数字个数是多少个,
并求出用这个最多的数字,作为挑选得数字的个数,然后这些个数字之和<=m,成立的方案数有多少种,
贪心+二维费用01背包,开始只想到贪心的如何购买到最多多少件,要求出购买的方案数,~~母鸡,
转载:
有n件物品,每件物品都有自己得价格,旅客一共有m块大洋。
第一个问题,旅客最多可以买多少件物品?请注意,这里是多少件,不是价值最大。所以这个非常好求,将所有的物品按照价值排序,先买便宜的,再买贵的。
贪心的思想。要注意一些边界问题
用这种方法,我们可以求出旅客最多买多少件物品,求出之后,物品的价格就有了两种属性,一种是钱数,一种是件数。也就是买一件物品需要的消耗是它的价格的钱数和1件物品的份额。在背包九讲中,这个叫做二维费用的背包问题。
如果是求最优方案(这些个金钱m和最多的件数可以得到的最大价值),这个问题依旧毫无压力。

但是现在的问题是求方案总数。
所以就可以转化为二位费用得背包问题  (重点,再看了整数拆分之后回过头来看这个问题,他是用的二位费用的01背包来求解最大的!!!方案数!!是方案数注意转移方程的含义,
就等于把一个数n划分为由若干个不同的整数之和得到的,那么就是每个数字只能选或者不选,而且那道题是1维费用(数字的大小,整数拆分三的第五问)把n当作为背包容量,1~n当作物品件数
先思考表达式的含义应该是数字i由前j个(1~j)不同的数组成得到的最大方案数 得dp[i][j]=dq

我们用dp[j][k]表示花费j元买k件物品的方案数,实际上很容易我们就可以得到dp[j][k]=dp[j][k]+dp[j-a[i]][k-1]。
关于这个方程有两个需要特别解释的地方,第一个这个是空间优化(dp[i][j][k]=dp[i-1][j][k]+dp[i-1][j-a[i]][k-1])后的方程,优化后的原理参见背包九讲第一讲01背
在考虑第i-1件物品后dp[j][k]的方案数。这个解释了这个dp[j][k]为什么可以直接继承过来。第二个需要解释的是,在dp[j][k]和dp[j-a[i]][k-1]中有没有相同的方案,即我们有没有冒着重复计算的风险将两者相加。答案是没有。
因为在dp[j-a[i]][k-1]这些方案中都有第i件物品,我们说过dp[j][k]实际上是dp[i-1][j][k],其中根本没有第i件物品的影子,所以两者不可能有重复的方案。

实际上如果了解第k大背包的算法的话,会对解决这道题有很大帮助。

几天了再回过头来看看这道题感觉算是个二位费用得01背包水题吧,把商品得价值和价值和件数都作为背包的属性,总的价值和贪心得得到得最多可以购买的数量作为背包得容量。
不过初始化问题还是要考虑注意一下为啥要这样得
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
int dp[1005][1005];
int w[1005];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        for(int i=0;i<n;++i)cin>>w[i];
        sort(w,w+n);
        int cnt=0,ans=0;
        for(int i=0;i<n;++i)
        {
            ans+=w[i],cnt++;
            if(ans>m)break;
        }
        if(ans>m) --cnt;
        if(!cnt){cout<<"Sorry, you can't buy anything."<<endl;continue;}
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=m;++i)dp[i][0]=1;
        for(int i=0;i<n;++i)
        {
            for(int j=m;j>=w[i];--j)
            {
                for(int k=cnt;k>=1;--k)
                dp[j][k]=dp[j][k]+dp[j-w[i]][k-1];
            }
        }
       // for(int i=0;i<=cnt;++i)cout<<dp[m][i]<<' ';
        cout<<"You have "<<dp[m][cnt]<<" selection(s) to buy with "<<cnt<<" kind(s) of souvenirs."<<endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值