洛谷01背包问题汇总

持续更新…

1. P1060 开心的金明

解题思路

最基本的01背包问题,可以不装满,其中 j j 表示总的钱, i i 表示第 i i 个商品, f [ j ] f[j] 表示用金额为 j j 的钱获得目标的最高价值。
f [ j ] = m a x ( f [ j ] , f [ j a [ i ] . v ] + a [ i ] . v a [ i ] . p ) f[j]=max(f[j],f[j-a[i].v]+a[i].v*a[i].p)

#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有点变化,这里的背包要求更加严格,即必须装满整个背包,不能有空余的空间。这里
f [ i ] [ j ] = f [ i 1 ] [ j ] + 1   j = = a [ i ] f[i][j]=f[i-1][j]+1, \ j==a[i]时;
f [ i ] [ j ] = f [ i 1 ] [ j ] + f [ i 1 ] [ j a [ i ] ]   j &gt; a [ i ] f[i][j]=f[i-1][j]+f[i-1][j-a[i]], \ j&gt;a[i]时;
f [ i ] [ j ] = f [ i 1 ] [ j ] ,   j &lt; a [ i ] f[i][j]=f[i-1][j], \ j&lt;a[i]时

#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背包问题也可以完成。如总的时间为 T T ,这时候每个商品的数量可化为有限的 T a [ i ] . t \frac{T}{a[i].t} ,然后再对所有商品利用01背包的思想来解决,时间复杂度为 O ( M i = 1 M T a [ i ] . t ) O( M\sum_{i=1}^{M} \frac{T}{a[i].t})
如果换一种思路不将每个商品的具体数量求出来,则需要对 f [ j ] f[j] 更新时的顺序进行调整。之前在更新 f [ j ] f[j] 时,对于每个 j j 从总的时间 T T 往回更新,这是为了保证在更新二维情况下的 f [ i ] [ j ] f[i][j] 时,之前的 f [ i ] [ j a [ i ] . v ] f[i][j-a[i].v] 未做改变,即保证第 i i 个商品只有两种状态,要么被选,要么不被选。但是现在的第 i i 个商品的数量是无限的,所以在更新 f [ i ] [ j ] f[i][j] 时,应该保证之前的 f [ i ] [ j a [ i ] . v ] f[i][j-a[i].v] 做了改变,即第 i i 个商品可以多次被选。
所以将原来从总的时间 T T 往回更新,换成 a [ i ] . v a[i].v T T 顺序更新即可。时间复杂度为 O ( M T ) O(MT)

#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背包的变种(加强版),对于题目中的每个垃圾分为吃或不吃两种状态,设 f [ i ] [ j ] f[i][j] 表示到第 i i 个垃圾达到高度为 j j 时剩下的时间。
(1)吃: f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i 1 ] [ j ] + a [ i ] . F ( a [ i ] . T a [ i 1 ] . T ) ) f[i][j]=max(f[i][j],f[i-1][j]+a[i].F-(a[i].T-a[i-1].T))
(2)不吃: f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i ] [ j a [ i ] . H ] ( a [ i ] . T a [ i 1 ] . T ) ) f[i][j]=max(f[i][j],f[i][j-a[i].H]-(a[i].T-a[i-1].T))
如果最后 j j 转化不到总高度 D D ,那么存活的最长时间= m a x ( f [ i ] [ j ] + a [ i ] . T ) max(f[i][j]+a[i].T) ,即当前时间 a [ i ] . T a[i].T 加上当前剩余的时间 f [ i ] [ j ] f[i][j]
最后注意按垃圾出现的时间升序排列。

#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;
}

猜你喜欢

转载自blog.csdn.net/zhuixun_/article/details/82833869