1. 程式人生 > >【01揹包的k值問題 HDU2639 HDU2126】

【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;
}