[洛谷P1507]NASA的食物计划 以及 对背包问题的整理

P1507 NASA的食物计划

题面

每个物品有三个属性,“所含卡路里”:价值\(v\),“体积”:限制1\(m_1\),以及“质量”:限制2\(m_2\),在n件物品中选择一部分,使得所选物品价值\(v\)之和最大。
同时要求这些物品的限制1\(m_1\)之和不超过限制1上限\(c_1\),限制2\(m_2\)之和不超过限制2上限\(c_2\)

格式

输入包括n+2行。
第一行包括两个整数\(c_1,c_2(c_1,c_2<400)\)
第二行包括一个整数\(n(n<50)\)
下面n行,每行包括三个整数\(m_1,m_2,v(m_1,m_2<400,v<500)\)

输出满足条件的最大的\(v\)之和

分析

看题目能大体猜到是一道背包题。但是与背包不一样的是,限制有两个。
类比普通背包问题中一般设\(dp[i]\)表示限制为\(i\)时的最优解,那么我们这里可以设\(dp[i][j]\)表示限制分别为\(i\)\(j\)时的最优解。
下面考虑转移方程式。由于选择一个物品会将两个限制都减少,所以可以得到转移式为\(dp[i][j]=max(dp[i][j],dp[i-m1[k]][j-m2[k]]+v[k])\)其中k表示考虑第k个物品。

代码

#include<bits/stdc++.h>
using namespace std;
int c1,c2,n;
int m1,m2,v;
int f[402][402];
int main(){
	cin>>c1>>c2>>n;
	for(int k=1;k<=n;k++){
		cin>>m1>>m2>>v;
		for(int i=c1;i>=m1;i--)
			for(int j=c2;j>=m2;j--){
				f[i][j]=max(f[i][j],f[i-m1][j-m2]+v);
			}
	}
	cout<<f[c1][c2];
}

后记

到这里,题已经做出来了。通过这两天刷的这些题,大家应该找到了背包问题的通解。在这里总结一下。

  1. 01背包 一个物品只能选一次 \(dp[i]=max(dp[i],dp[i-m[k]]+v[k])\ i=c...m[k]\ O(nc)\)
  2. 完全背包 一个物品可以选无限次 \(dp[i]=max(dp[i],dp[i-m[k]]+v[k])\ i=m[k]...c\ O(nc)\)
  3. 多维背包 有多个限制 \(dp[i1]...[in]=max(dp[i1]...[in],dp[i1-m1[k]]...[in-mn[k]]+v[k])\ O(nc1...cn)\)
  4. 多重背包 一个物品只能选有限次 可以把一个物品分解为多个同样属性的物品,然后用01背包求解\(O(\sum times[i]\ *c)\)(再难一点的做法是二进制优化\(O(\sum log_2times[i]\ *c)\),甚至单调队列\(O(nc)\)
  5. 超大背包 限制特别大,但是每个物品价值较低 \(dp[i]=min(dp[i],dp[i-v[k]]+m[k])\)其中\(dp[i]\)表示达到价值i时的最小质量
posted @ 2019-03-12 20:28  water_lift  阅读(365)  评论(0编辑  收藏  举报