持续更新…
1. P1060 开心的金明
解题思路
最基本的01背包问题,可以不装满,其中
表示总的钱,
表示第
个商品,
表示用金额为
的钱获得目标的最高价值。
#include<iostream>
#include<cstdio>
using namespace std;
//f[j]=max(
struct node{
int v,p;
}a[30];
int f[30100];
int main(int argc, char** argv) {
int N,m;
scanf("%d%d",&N,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i].v,&a[i].p);
}
for(int i=1;i<=m;i++){
for(int j=N;j>=a[i].v;j--){
f[j]=max(f[j],f[j-a[i].v]+a[i].v*a[i].p);
}
}
printf("%d\n",f[N]);
return 0;
}
2. P1164 小A点菜
解题思路
参考自:https://www.luogu.org/blog/user31798/solution-p1164
与1有点变化,这里的背包要求更加严格,即必须装满整个背包,不能有空余的空间。这里
#include<iostream>
#include<cstdio>
using namespace std;
int a[110],f[110][10010]={0};
int main(int argc, char** argv) {
int n,m,ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(j==a[i]) f[i][j]=f[i-1][j]+1;//==
if(j>a[i]) f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
if(j<a[i]) f[i][j]=f[i-1][j];
}
}
printf("%d\n",f[n][m]);
return 0;
}
3. P1064 金明的预算方案
解题思路
参考自https://www.luogu.org/blog/user20197/solution-p1064
分层dp,有依赖性的背包问题,注意到这里的附件必须与主件结合才能使用。首先直接对所有主件按一般背包问题进行处理;然后对于特定的主件引入附件再采用背包的思想进行处理,若引入附件的结果优于未引入主件的背包问题,则更新最优的结果。
#include<iostream>
#include<cstdio>
using namespace std;
struct node{
int v,p,q;
}a[65],b[65][1000]; //a记录所有信息,b记录附件信息
int N,m;
int f[32100];
int index[1000];
int main(int argc, char** argv) {
scanf("%d%d",&N,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a[i].v,&a[i].p,&a[i].q);
if(a[i].q!=0){//附件
index[a[i].q]++;
b[a[i].q][index[a[i].q]].v=a[i].v;
b[a[i].q][index[a[i].q]].p=a[i].p;
}
}
for(int i=1;i<=m;i++){
for(int j=N;a[i].q==0&&j>=a[i].v;j--){
f[j]=max(f[j],f[j-a[i].v]+a[i].v*a[i].p);//主件
if(j>=a[i].v+b[i][1].v)//附件1
f[j]=max(f[j],f[j-a[i].v-b[i][1].v]+a[i].v*a[i].p+b[i][1].v*b[i][1].p);
if(j>=a[i].v+b[i][2].v)//附件2
f[j]=max(f[j],f[j-a[i].v-b[i][2].v]+a[i].v*a[i].p+b[i][2].v*b[i][2].p);
if(j>=a[i].v+b[i][1].v+b[i][2].v)//附件1,2
f[j]=max(f[j],f[j-a[i].v-b[i][1].v-b[i][2].v]+a[i].v*a[i].p+b[i][1].v*b[i][1].p+b[i][2].v*b[i][2].p);
}
}
printf("%d\n",f[N]);
return 0;
}
4. P1616 疯狂的采药
解题思路
完全背包问题,因为每个商品的数量理论上来说是无限的。这时候如果直接转成01背包问题也可以完成。如总的时间为
,这时候每个商品的数量可化为有限的
,然后再对所有商品利用01背包的思想来解决,时间复杂度为
。
如果换一种思路不将每个商品的具体数量求出来,则需要对
更新时的顺序进行调整。之前在更新
时,对于每个
从总的时间
往回更新,这是为了保证在更新二维情况下的
时,之前的
未做改变,即保证第
个商品只有两种状态,要么被选,要么不被选。但是现在的第
个商品的数量是无限的,所以在更新
时,应该保证之前的
做了改变,即第
个商品可以多次被选。
所以将原来从总的时间
往回更新,换成
往
顺序更新即可。时间复杂度为
。
#include <iostream>
#include<cstdio>
using namespace std;
struct node{
int t,v;
}a[10100];
int f[100010];//M,T
int main(int argc, char** argv) {
int T,M;
scanf("%d%d",&T,&M);
for(int i=1;i<=M;i++) scanf("%d%d",&a[i].t,&a[i].v);
for(int i=1;i<=M;i++){
for(int j=a[i].t;j<=T;j++){//顺序更新
f[j]=max(f[j],f[j-a[i].t]+a[i].v);
}
}
printf("%d\n",f[T]);
return 0;
}
5. P1156 垃圾陷阱
解题思路:
非常有意思的一道题,是01背包的变种(加强版),对于题目中的每个垃圾分为吃或不吃两种状态,设
表示到第
个垃圾达到高度为
时剩下的时间。
(1)吃:
(2)不吃:
如果最后
转化不到总高度
,那么存活的最长时间=
,即当前时间
加上当前剩余的时间
。
最后注意按垃圾出现的时间升序排列。
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define minv -233233233
struct node{
int t,f,h;
bool operator < (const node &b) {
return t<b.t;//时间升序;
}
}a[110];
int d,g;
int f[110][110];//i商品,j高度,f[i][j]剩下的生命时间
int main(int argc, char** argv) {
scanf("%d%d",&d,&g);
for(int i=1;i<=g;i++){
scanf("%d%d%d",&a[i].t,&a[i].f,&a[i].h);
}
sort(a+1,a+g+1);
for(int i=1;i<=110;i++) for(int j=0;j<=110;j++) f[i][j]=minv;
f[0][0]=10; int flag=0;
a[0].f=0;a[0].h=0;a[0].t=0;
for(int i=1;i<=g;i++){
for(int j=0;j<=d;j++){
if(f[i-1][j]-(a[i].t-a[i-1].t)>=0)
f[i][j]=max(f[i][j],f[i-1][j]+a[i].f-(a[i].t-a[i-1].t));//吃
if(j-a[i].h>=0&&f[i-1][j-a[i].h]-(a[i].t-a[i-1].t)>=0){
f[i][j]=max(f[i][j],f[i-1][j-a[i].h]-(a[i].t-a[i-1].t));//不吃
if(j==d){//如果能逃出来,一定是由其他状态转化而来
flag=1;
printf("%d\n",a[i].t);
return 0;
}
}
}
}
if(!flag){
int ans=-1;
for(int i=1;i<=g;i++){
for(int j=0;j<=d;j++){
if(f[i][j]!=minv){
ans=max(ans,f[i][j]+a[i].t);
}
}
}
printf("%d\n",ans);
}
return 0;
}