DP基础(背包问题)

目录

01背包模板

多重背包模板

完全背包模板

例题

A:HDU-2546 饭卡

B:HDU-1171 Big Event in HDU

C:HDU-2602 Bone Collector

D:HDU-2639 Bone Collector II

E:HDU-2955 Robberies:

F:HDU-2197

G:HDU-1114


背包的问题可先参考视频(背包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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值