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