刷题遇到的背包问题一定不是一个指向很明显的题目甚至大多数时间都需要我们在模板上做一些处理才能得到正解 那么下面简单介绍一些刷题遇到的变形题目
1.求不超过要求高度的最高累加高度
题目看起来很绕没关系我们直接上例题
poj-3628
这个题目是价值和重量一样的背包问题 没什么好说的我们任然按照背包问题做但是dp[i][j]表示的就是前i个牛高度和不超过j的最大高度和
#include<iostream>
#include<cstring>
using namespace std;
int dp[1000002];
int high[20];
long long sum;
int main()
{
int n,m;
while(cin>>n>>m)
{
long long sum=0;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>high[i];
sum+=high[i];
}
for(int i=1;i<=n;i++)
{
for(int j=sum;j>=high[i];j--)
{
dp[j]=max(dp[j],dp[j-high[i]]+high[i]);
}
}
for( int k=0;k<=sum;k++)
{
if(dp[k]>=m)
{
cout<<dp[k]-m<<endl;
break;
}
}
}
}
牛客1020
这个题目乍一看根本想不到用动态规划来解决 思路是这样的我们先把所有的苹果的质量和算出来用sum来表示 那么如果想要平均分配那么一个人分到的就是sum>>1 但是sum>>1不一定能刚好取到 那么这里我们就可以类比上一个题目的思想dp[i][j]表示前i个苹果总和小于等于j的最大质量和 我们把求出dp[n][sum>>2] n表示苹果总数 这样一来就是最可能的平均分配了 完整代码如下:
#include<bits/stdc++.h>
using namespace std;
int dp[11000]={0};
int value[110];
int sum;
int n;
int ans;
void slove(){
for(int i=1;i<=n;i++){
for(int j=sum;j>=value[i];j--){
dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
}
}
}
int main(){
cin>>n;
sum=0;
for(int i=1;i<=n;i++){
cin>>value[i];
sum=sum+value[i];
}
int temp=sum;
sum>>=1;
slove();
int k=max(temp-dp[sum],dp[sum]);
int d=min(temp-dp[sum],dp[sum]);
cout<<d<<" "<<k<<endl;
}
2.有其他约束的背包问题
1.牛牛的旅游纪念品
首先我们先考虑只取一个物品那么这个物品可以是1,2,3,.....,n 取第二个物品因为相隔必须大于等于k 所以我们能取到的所有可能为k+1,k+2,k+3....,n; 值得注意的是受欢迎程度可能为负数且我们必须取够m个物品 因为可能为负数我们就不能把数组初始化为0而是初始化为-0x3f3f3f3f 因为当取第一个物品时候比较特殊所以我们把他们单独处理 而其余的我们从k+1开始循环dp[i][j]当在前j个礼品里取了i个时候的最大欢迎程度转移方程为dp[i][j]=max(dp[i][j-1],dp[i-1][j-k])
include<bits/stdc++.h>
using namespace std;
int n,m,k;
int dp[10010][110];
int value[10010];
long long INF=-0x3f;
int slove()
{
dp[1][0]=INF;
for(int i=1;i<=n;i++)
{
dp[1][i]=max(dp[1][i-1],value[i]);
}
for(int i=2;i<=m;i++)
{
for(int j=(i-1)*k+1;j<=n;j++)
{
if(j==(i-1)*k+1)dp[i][j]=dp[i-1][j-k]+value[j];
else dp[i][j]=max(dp[i][j-1],dp[i-1][j-k]+value[j]);
}
}
return dp[m][n];
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)cin>>value[i];
cout<<slove()<<endl;
}
2有数量限制的01背包问题
在01背包问题的基础上加上一个约束背包里至多放t个物品那么思路为:dp[k][j]表示体积大小为j允许放k个物品的背包的最大价值那么转移方程为dp[k][j]=max(dp[[k-1][j],dp[k-1][j-vol[i]]+val[i];其中vol[i]表示第i个物品的体积大小 val[i]表示第i个物品的价值 完整代码:
#include<bits/stdc++.h>
using namespace std;
int dp[1000][1000];
int val[1000],vol[1000];
int n,v,t;
int slove(){
int ans=0;
for(int i=1;i<=n;i++){
for(int j=v;j>=vol[i];j--){
for(int k=1;k<=t;k++){
dp[k][j]=max(dp[k-1][j],dp[k-1][j-vol[i]]+val[i]);
ans=max(ans,dp[k][j]);
}
}
}
return ans;
}
int main(){
cin>>n>>v>>t;
for(int i=1;i<=n;i++)cin>>val[i]>>vol[i];
cout<<slove()<<endl;
}
3.需要对物品排序的背包问题
1.美味佳肴
此题目是一个变形的背包问题这里一开始容易被忽略的点就是 做不同菜肴的顺序会影响着菜肴的美味程度 那么我们首先要确定做菜的顺序才能使用0/1背包问题模板
那么确定顺序呢 首先我们设两个菜肴分别是 x,y a表示美味度 b表示速率 c表示做菜时间 那么如果x比y先做 那么一定有 a_x-b_x*c_x+a_y-b_y*(c_x+c_y)>= a_y-b_y*c_y+a_x-b_x*(c_x+c_y)
化简可得 b_x*c_y>=b_y*c_x 那么就依据此来排序即可 之后我们就可以按照0/1背包问题来做了dp[i][j]表前j秒做前j个菜的最大美味度
#include<bits/stdc++.h>
using namespace std;
long long dp[1000100];
struct node{
int a,j,c;
}cook[1000100];
long long cai[1000100];
long long n,m,T;
bool cmp(node x,node y){
return x.c*cai[y.j]<y.c*cai[x.j];
}
long long slove(){
long long ans=-1e15;
dp[0]=0;
for(int i=1;i<=m;i++){
for(int j=T;j>=cook[i].c;j--){
if(j>=cook[i].c)dp[j]=max(dp[j],dp[j-cook[i].c]+cook[i].a-j*cai[cook[i].j]);
}
}
for(int i=1;i<=T;i++)if(ans<dp[i])ans=dp[i];
return ans;
}
int main(){
cin>>n>>m>>T;
memset(dp,-0x3f,sizeof(dp));
for(int i=1;i<=n;i++)cin>>cai[i];
for(int j=1;j<=m;j++)cin>>cook[j].j>>cook[j].a>>cook[j].c;
sort(cook+1,cook+m+1,cmp);
cout<<slove()<<endl;
}