揹包九講 11題

聚聚視頻講的太好了

前六講 最後三講

 

題目全在這了

 01揹包問題

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,v,w;i<=n;++i){
		cin>>v>>w;
		for(int j=m;j>=v;--j) dp[j]=max(dp[j],dp[j-v]+w);
	}
	cout<<dp[m]<<endl;
	return 0;
}

完全揹包

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,v,w;i<=n;++i){
		cin>>v>>w;
		for(int j=v;j<=m;++j) dp[j]=max(dp[j],dp[j-v]+w);
	}
	cout<<dp[m]<<endl;
	return 0;
}

多重揹包問題 I

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=105;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,v,w,s;i<=n;++i){
		cin>>v>>w>>s;
		for(int j=m;j>=v;--j){
			for(int k=0;k<=s && k*v<=j;++k){
				dp[j]=max(dp[j],dp[j-k*v]+k*w);
			}
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

多重揹包問題 II

二進制拆分優化,然後作爲01揹包來考慮即可,將s拆成2進製表示,最後多的那個部分單獨拿出來。 

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=2e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
struct node{
	int v,w;
};
vector<node> a;
int n,m,dp[N];
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,v,w,s;i<=n;++i){
		cin>>v>>w>>s;
		for(int k=1;k<=s;k*=2){
			a.pb({v*k,w*k});
			s-=k;
		}
		if(s) a.pb({v*s,w*s});
	} 
	for(auto it:a){
		int v=it.v,w=it.w;
		for(int j=m;j>=v;--j) dp[j]=max(dp[j],dp[j-v]+w);
	}
	cout<<dp[m]<<endl; 
	return 0;
}

多重揹包問題 III

單調隊列優化,對於一個體積爲v的物品,我們考慮到他的更新,將體積%v的值進行分組,一個組之間纔會有遞推關係,而不同的組之間是不會有更新和遞推關係的。考慮對於一個dp[j] 是從上一層dp[j-v]+w,dp[j-2*v]+2*w…….更新來,那對於dp[j+v]就是從上一層dp[j]+w,dp[j-v]+2*w…..更新而來,其實就是對於dp[j],dp[j+v]就是平移的一格,然後總體+w;但是原來數的大小關係並沒有發生改變,所以這裏用單調隊列維護即可,只是加的x*w發生了改變,這個我們可以通過計算與j差幾個v,就可以得出了嘛。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=2e5+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,dp[N][2],q[N];
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	bool fg=0;
	int tt=-1,hh=0; 
	for(int i=1,v,w,s;i<=n;++i){
		cin>>v>>w>>s;
		for(int j=0;j<v;++j){//將體積按%v的值進行分類 
			tt=-1,hh=0;
			for(int k=j;k<=m;k+=v){
				dp[k][fg]=dp[k][fg^1];
				while(hh<=tt && k-s*v>q[hh]) hh++;
				if(hh<=tt) dp[k][fg]=max(dp[k][fg],dp[q[hh]][fg^1]+(k-q[hh])/v*w);
				while(hh<=tt && dp[q[tt]][fg^1]-(q[tt]-j)/v*w <= dp[k][fg^1]-(k-j)/v*w) tt--; 
				q[++tt]=k;
			}	
		}
		fg^=1;
	}
	cout<<dp[m][fg^1]<<endl;
	return 0;
}

混合揹包問題

即包含了01揹包,完全揹包和多重揹包,這裏可以將多重揹包拆分成01揹包,然後我們只需要考慮01揹包和完全揹包,這裏兩個注意循環的方向即可。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
struct node{
	int type,v,w;
};
vector<node> a;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,v,w,s;i<=n;++i){
		cin>>v>>w>>s;
		if(s==-1) a.pb({-1,v,w});//01揹包 
		else if(s==0) a.pb({0,v,w});//多重揹包 
		else{//完全揹包拆成01揹包 
			for(int k=1;k<=s;k*=2){
				a.pb({-1,v*k,w*k});
				s-=k; 
			} 
			if(s) a.pb({-1,v*s,w*s});
		}
	}
	int v,w;
	for(auto it:a){
		v=it.v,w=it.w;
		if(it.type==-1){
			for(int i=m;i>=v;--i) dp[i]=max(dp[i],dp[i-v]+w);
		}
		else{
			for(int i=v;i<=m;++i) dp[i]=max(dp[i],dp[i-v]+w);
		}
	} 
	cout<<dp[m]<<endl;
	return 0;
}

二維費用的揹包問題

多了一維重量,這裏直接考慮多循環一層重量即可,01揹包從大到小進行更新即可。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N][N],n,V,M;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>V>>M;
	for(int i=1,v,m,w;i<=n;++i){
		cin>>v>>m>>w;
		for(int j=V;j>=v;--j){
			for(int k=M;k>=m;--k){
				dp[j][k]=max(dp[j][k],dp[j-v][k-m]+w);
			}
		} 
	}
	cout<<dp[V][M]<<endl;
	return 0;
}

分組揹包問題

多一重循環,來判斷選這一組裏的哪一個

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e2+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,v[N],w[N],dp[N];
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,s;i<=n;++i){
		cin>>s;
		for(int j=1;j<=s;++j) cin>>v[j]>>w[j];
		for(int j=m;j>=0;--j){
			for(int k=1;k<=s;++k){
				if(j>=v[k]) dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
			}
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

有依賴的揹包問題

dp[i][j]:當選i爲節點,空間不大於j的最大值

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e2+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N][N];//dp[i][j]:當選i爲節點,空間不大於j的最大值
int n,m,v[N],w[N],root; 
vector<int> G[N];
il void dfs(int u){
	for(auto son:G[u]){
		dfs(son);
		for(int j=m-v[u];j>=0;--j){//給u空出v[u]空間,剩下的分給兒子 
			for(int k=0;k<=j;++k){
				dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[son][k]);
			}
		}
	}
	for(int j=m;j>=v[u];--j) dp[u][j]=dp[u][j-v[u]]+w[u];
	for(int j=v[u]-1;j>=0;--j) dp[u][j]=0;
}
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1,np;i<=n;++i){
		cin>>v[i]>>w[i]>>np;
		if(np==-1) root=i;
		else G[np].pb(i);
	}
	dfs(root);
	cout<<dp[root][m]<<endl;
	return 0;
}

揹包問題求方案數

這裏初始化不能隨便的默認爲0了,因爲這裏需要更新方案數,前面的dp[i]表示體積不大於i的最大價值,(這和全部初始化爲0有關,因爲如果當空間爲k時取得最大價值,因爲我們將dp[m-k]初始化爲0,所以也可以得到最值,但實際空間不爲m)這樣的話不利於更新方案數。現在初始話只將dp[0]定義爲0,其他的定義爲-infdp[i]表示空間恰好爲i時的最大價值,這樣是符合實際情況的,最後的mx是要掃一遍數組的,因爲最大價值並不一定在最大空間取得。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e3+5; 
il int Add(int &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(int &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],num[N];
//dp[i]:體積爲i時的最大價值,num[i]:滿足體積爲i最大價值的方案數 
int n,m;
int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	num[0]=1;
	for(int i=1;i<=m;++i) dp[i]=-inf;
	for(int i=1,v,w;i<=n;++i){
		cin>>v>>w;
		for(int j=m;j>=v;--j){
			int mx,mxnum=0;
			mx=max(dp[j],dp[j-v]+w);
			if(mx==dp[j]) Add(mxnum,num[j]);
			if(mx==dp[j-v]+w) Add(mxnum,num[j-v]);
			dp[j]=mx,num[j]=mxnum;
		}
	}
	int ans=-inf,all=0;
	for(int j=0;j<=m;++j) ans=max(ans,dp[j]);
	for(int j=0;j<=m;++j){
		if(dp[j]==ans) Add(all,num[j]);
	}
	cout<<all<<endl;
	return 0;
}

揹包問題求具體方案

因爲題目要求字典序,所以i從大到小着來,然後從1到n從小到大貪心的考慮。視頻中代碼少加了一個nv-v[i]的判斷。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(int &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(int &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,dp[N][N],v[N],w[N];

int main(){
	std::ios::sync_with_stdio(0);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>v[i]>>w[i];
	//從後往前更新 
	for(int i=n;i>=1;--i){
		for(int j=0;j<=m;++j){
			if(j>=v[i]) dp[i][j]=max(dp[i+1][j],dp[i+1][j-v[i]]+w[i]);
			else dp[i][j]=dp[i+1][j];
		}
	}
	int nv=m;
	for(int i=1;i<=n;++i){
		if(nv-v[i]>=0 && dp[i][nv]==dp[i+1][nv-v[i]]+w[i]){
			cout<<i<<" ";
			nv-=v[i];
		}
	}
	return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章