DP基础(背包问题)
目录
背包的问题可先参考视频(背包DP)
01背包模板
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn],size1[maxn],dp[maxn]; //f[x]表示背包容量为x时的最大价值
int main()
{
int t;
cin>>t;
while(t--){
memset(dp,0,sizeof(dp));
int n,x;
cin>>n>>x; //n为物品种类,x为容量
for(int i=1;i<=n;i++)
cin>>value[i];
for(int i=1;i<=n;i++)
cin>>size1[i];
for(int i=1;i<=n;i++)
for(int j=x;j>=size1[i];j--)
dp[j]=max(dp[j],dp[j-size1[i]]+value[i]);
cout<<dp[x]<<endl;
}
return 0;
}
多重背包模板
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn],size1[maxn],dp[maxn],bag[maxn];
int main()
{
int t,nvalue,n;
cin>>t;
while(t--){
memset(dp,0,sizeof(dp));
cin>>nvalue>>n;
for(int i=0;i<n;i++)
cin>>value[i]>>size1[i]>>bag[i];
for(int i=0;i<n;i++){
for(int j=1;j<=bag[i];j++)
for(int k=nvalue;k>=value[i];k--)
dp[k]=max(dp[k],dp[k-value[i]]+size1[i]);
}
cout<<dp[nvalue]<<endl;
}
return 0;
}
完全背包模板
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <sstream>
#include <cstdlib>
#define ll long long
using namespace std;
const int maxn = 1e6+10;
const int INF=0x3f3f3f3f;
int a[600],b[600],dp[1000005];
int main()
{
int t,x,y,n;
cin>>t;
while(t--){
int w;
cin>>w; //背包容量
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i]>>b[i];
for(int i=0;i<=w;i++) //初始化分两种情况:1、如果背包要求正好装满则初始化 f[0] = 0, f[1~w]=INF; 2、如果不需要正好装满 f[0~w] = 0;
dp[i]=INF;
dp[0]=0;
for(int i=0;i<n;i++)
for(int j=b[i];j<=w;j++){
dp[j]=max(dp[j],dp[j-b[i]]+a[i]);
}
}
return 0;
}
例题
A:HDU-2546 饭卡:n道菜,选出最贵的,放到最后买,然后在保留5元的情况下,用n-5元买剩下的才转化为背包问题。AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn],size1[maxn],dp[maxn];
int main()
{
int n;
while(cin>>n&&n!=0){
memset(dp,0,sizeof(dp));
int x;
for(int i=1;i<=n;i++)
cin>>value[i];
cin>>x;
if(x<5){
cout<<x<<endl;
continue;
}
sort(value+1,value+1+n);
for(int i=1;i<n;i++){
for(int j=x-5;j>=value[i];j--)
dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
}
cout<<x-dp[x-5]-value[n]<<endl;
}
return 0;
}
B:HDU-1171 Big Event in HDU:多重背包问题,把背包容量当作sum/2(sum为所有物品的体积和)即可,AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e6+7;
const int INF=0x3f3f3f3f;
int value[160],size1[160],dp[maxn],bag[60];
int main()
{
int sum,sum1,n;
while(cin>>n&&n>0)
{
sum=0;
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++)
{
cin>>size1[i]>>bag[i];
sum+=size1[i]*bag[i];
}
sum1=sum/2;
for(int i=0; i<n; i++)
{
for(int j=0; j<bag[i]; j++)
for(int k=sum1; k>=size1[i]; k--)
{
dp[k]=max(dp[k],dp[k-size1[i]]+size1[i]);
}
}
cout<<sum-dp[sum1]<<' '<<dp[sum1]<<endl;
}
return 0;
}
C:HDU-2602 Bone Collector:01背包模板题,直接上代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn],size1[maxn],dp[maxn];
int main()
{
int t;
cin>>t;
while(t--){
memset(dp,0,sizeof(dp));
int n,x;
cin>>n>>x;
for(int i=1;i<=n;i++)
cin>>value[i];
for(int i=1;i<=n;i++)
cin>>size1[i];
for(int i=1;i<=n;i++)
for(int j=x;j>=size1[i];j--)
dp[j]=max(dp[j],dp[j-size1[i]]+value[i]);
cout<<dp[x]<<endl;
}
return 0;
}
D:HDU-2639 Bone Collector II:第k优解问题。这一题感觉不是很容易想到,要求背包第K大的价值。我们通俗的01背包是保留当前状态下的最好决策,比如最大或最小。但是这里要求第K大的价值,我们用单纯的01背包肯定是算不出来的,因为有一些不优的状态被我们省掉了,但是这些状态可能会被第K大的取到,所以我们要先办法保留状态。 我们这里用三个数组,cnt1,cnt2和用来DP的数组D。 cnt1[k]表示取第i件物品时,第k大的价值。 cnt2[k]表示不取第i件物品时,第k大的价值。 D[j][k]表示背包容量为j时,第k大的价值。 我们可以看到,在第i,j个状态时,我们可以用在i-1状态时的D数组推得cnt1,和cnt2。再用cnt1,2来推当前的D数组。写出来是这样的: cnt1[k]=d[j-v[i]][k]+c[i]; cnt2[k]=d[j][k]; D=cnt1和cnt2的合并。
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn = 1005;
int main()
{
int T;
scanf("%d", &T);
int dp[maxn][33], val[maxn], vol[maxn], A[33], B[33];
while (T--)
{
int n, v, k;
scanf("%d %d %d", &n, &v, &k);
int i, j, kk;
for (i=1; i<=n; i++) scanf("%d", &val[i]);
for (i=1; i<=n; i++) scanf("%d", &vol[i]);
memset(dp, 0, sizeof(dp));
int a, b, c;
for (i=1; i<=n; i++)
for (j=v; j>=vol[i]; j--)
{
for (kk=1; kk<=k; kk++)
{
A[kk] = dp[j-vol[i]][kk] + val[i];
B[kk] = dp[j][kk];
}
A[kk] = -1, B[kk] = -1;
a = b = c = 1;
while (c<=k && (A[a] != -1 || B[b] != -1))
{
if (A[a] > B[b])
dp[j][c] = A[a++];
else
dp[j][c] = B[b++];
if (dp[j][c] != dp[j][c-1])
c++;
}
}
printf("%d\n", dp[v][k]);
}
return 0;
}
E:HDU-2955 Robberies:给大家推荐个非常巧的解法Robberies题解,代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn];
double size1[maxn],dp[maxn];
int main()
{
int t;
cin>>t;
while(t--){
double x,sum=0;
int n;
cin>>x>>n;
for(int i=1;i<=n;i++){
cin>>value[i]>>size1[i];
sum+=value[i];
}
for(int i=0;i<=sum+1;i++){
dp[i]=0.0;
}
dp[0]=1;
for(int i=1;i<=n;i++){
for(int j=sum;j>=value[i];j--)
dp[j]=max(dp[j],dp[j-value[i]]*(1-size1[i]));
}
for(int i=sum;i>=0;i--){
if((1-dp[i])<=x){
cout<<i<<endl;
break;
}
}
}
return 0;
}
F:HDU-2197 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活:多重背包的模板题,AC代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
int value[maxn],size1[maxn],dp[maxn],bag[maxn];
int main()
{
int t,nvalue,n;
cin>>t;
while(t--){
memset(dp,0,sizeof(dp));
cin>>nvalue>>n;
for(int i=0;i<n;i++)
cin>>value[i]>>size1[i]>>bag[i];
for(int i=0;i<n;i++){
for(int j=1;j<=bag[i];j++)
for(int k=nvalue;k>=value[i];k--)
dp[k]=max(dp[k],dp[k-value[i]]+size1[i]);
}
cout<<dp[nvalue]<<endl;
}
return 0;
}
G:HDU-1114 Piggy-Bank:完全背包的模板题,AC代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
const int maxn = 1e6+10;
const int INF=0x3f3f3f3f;
int a[600],b[600],dp[1000005];
int main()
{
int t,x,y,n;
cin>>t;
while(t--){
cin>>x>>y;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i]>>b[i];
int w;
w=y-x;
for(int i=0;i<=w;i++)
dp[i]=INF;
dp[0]=0;
for(int i=0;i<n;i++)
for(int j=b[i];j<=w;j++){
dp[j]=min(dp[j],dp[j-b[i]]+a[i]);
}
if(dp[w] == INF)
printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",dp[w]);
}
return 0;
}