dp day1-背包

01背包

洛谷p1048采药

01背包对于每一个物品,在当前的s可装下当前物品时比较装下该物品的总价值和不装该物品的价值。

(经典题+1)pascal也做过,省空间的做法如下

#include<bits/stdc++.h>
using namespace std;
int m,t,s[1010]={0},ma=0;
struct plant
{
	int time,value;
};
plant p[110];
int main()
{
	scanf("%d%d",&t,&m);
	for(int i=1;i<=m;i++)scanf("%d%d",&p[i].time,&p[i].value);
	for(int j=1;j<=m;j++)//枚举每棵草药
	for(int i=t;i>=p[j].time;i--)s[i]=max(s[i],s[i-p[j].time]+p[j].value);//从后往前避免草药重复选择
	for(int i=1;i<=t;i++)if(s[i]>ma)ma=s[i];//其实可以直接输出s[t],emmm...
	printf("%d",ma);
	return 0;
}
洛谷p1466集合

一个计算装满01背包的方案数的问题,和递推有相似之处(似乎就是递推?——蒟蒻的猜测)

#include<bits/stdc++.h>
using namespace std;
int n,t,sum=0;
long long s[10000]={0};
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)sum+=i;
	if(sum%2)
	{
		printf("0\n");
		return 0;
	}
	sum=sum/2; 
	s[0]=1;
	for(int j=1;j<=n;j++)
	for(int i=sum;i>=j;i--)s[i]=s[i]+s[i-j];
	printf("%lld",s[sum]/2);//输出方案要/2!老是卡在这种地方真是该做俯卧撑了
	return 0;
}
完全背包

洛谷p1616疯狂地采药

和01背包很相似,不多讲

#include<bits/stdc++.h>
using namespace std;
int m,t;
long long s[100010]={0},ma=0;
struct plant
{
	int time,value;
};
plant p[10010];
int main()
{
	scanf("%d%d",&t,&m);
	for(int i=1;i<=m;i++)scanf("%d%d",&p[i].time,&p[i].value);
	for(int j=1;j<=m;j++)
	for(long long i=p[j].time;i<=t;i++)s[i]=max(s[i],s[i-p[j].time]+p[j].value);//和01背包相比,只是换了一下循环的起始和结束
	for(long long i=1;i<=t;i++)if(s[i]>ma)ma=s[i];
	printf("%lld",ma);
	return 0;
}
洛谷p1474货币系统

也是完全背包,和1446对应

#include<bits/stdc++.h>
using namespace std;
int v,n,t,sum=0,a[30]={0}; 
long long s[10010]={0};
int main()
{
	scanf("%d%d",&v,&n);
	for(int i=1;i<=v;i++)scanf("%d",&a[i]);
	s[0]=1;
	for(int j=1;j<=v;j++)
	for(int i=a[j];i<=n;i++)s[i]=s[i]+s[i-a[j]];
	printf("%lld",s[n]);
	return 0;
}
洛谷p2725邮票

完全背包+1,但是常数太大也不行。

部分TLE版本

#include<bits/stdc++.h>
using namespace std;
int k,n,sum=0,a[30]={0}; 
long long s[2000010][2]={0},t=0;//s[i][0]存是否可以组成i,s[i][1]存最少邮票数
long long mi(long x,long y)
{
	if(y==0)return x;
	if(x<y)return x;
	else return y;
}

int main()
{
	scanf("%d%d",&k,&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	s[0][0]=1;s[0][1]=0;
	for(int j=1;j<=n;j++)
	for(int i=a[j];i<=10000*k;i++)
	if(s[i-a[j]][0]>0)s[i][0]=1,s[i][1]=mi(s[i-a[j]][1]+1,s[i][1]);
	for(int i=1;i<=10000*k;i++)if(s[i][1]>k||s[i][0]==0){t=i-1;break;}
	printf("%lld",t);
	return 0;
}
AC版本
#include<bits/stdc++.h>
using namespace std;
int k,n,sum=0,a[100]={0},t; 
long long s[2000010];
long long mi(long x,long y)
{
	if(y==0)return x;
	if(x<y)return x;
	else return y;
}

int main()
{
	scanf("%d%d",&k,&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=10000*k;i++)s[i]=233;//初始化减少了一个判断,减少了数组的一维
	s[0]=0;
        for(int j=1;j<=n;j++)
	for(int i=a[j];i<=10000*k;i++)
	if(s[i-a[j]]+1<=k)s[i]=mi(s[i-a[j]]+1,s[i]);//常数大概少了一半......
	for(int i=1;i<=10000*k;i++)if(s[i]>k){t=i-1;break;}
	printf("%d",t);
	return 0;
}

多重背包

洛谷p1776宝物筛选

先把一个物品拆成了多个物品试着做,光荣TLE

#include<bits/stdc++.h>
using namespace std;
int n,w,s[400010]={0},ma=0,t=0;
struct sub
{
	int wei,num,val;
};
sub a[10010];
struct subject
{
    int weight,value;
};
subject b[100010];
int main()
{
	scanf("%d%d",&n,&w);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i].val,&a[i].wei,&a[i].num);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=a[i].num;j++)b[++t].weight=a[i].wei,b[t].value=a[i].val;//物品拆分
	for(int j=1;j<=t;j++)
	for(int i=w;i>=b[j].weight;i--)s[i]=max(s[i],s[i-b[j].weight]+b[j].value);
	for(int i=1;i<=w;i++)if(s[i]>ma)ma=s[i];
	printf("%d",ma);
	return 0;
}
然后就是多重背包的重点了,任意正整数n都可以被拆分为1,2,4,...,2^(k-1),n[i]-2^k+1(k是满足n[i]-2^k+1>0的最大整数),这些数可以组成1到n中任意一个数。

#include<bits/stdc++.h>
using namespace std;
int n,w,s[400010]={0},ma=0;
struct sub
{
	int wei,num,val;
};
sub a[10010];
struct subject
{
    int weight,value;
};
subject t;
int main()
{
	scanf("%d%d",&n,&w);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i].val,&a[i].wei,&a[i].num);
	for(int i=1;i<=n;i++)
    {
    	int k=1;
    	while(k<=a[i].num)//玄学拆分
    	{
    		t.weight=a[i].wei*k;
    		t.value=a[i].val*k;
    		a[i].num-=k;
    		k=k<<1;
    		for(int i=w;i>=t.weight;i--)s[i]=max(s[i],s[i-t.weight]+t.value);//直接01背包形式处理
    	}
    	if(a[i].num>0)
		{
		    t.weight=a[i].wei*a[i].num;t.value=a[i].val*a[i].num;
		    for(int i=w;i>=t.weight;i--)s[i]=max(s[i],s[i-t.weight]+t.value);//多余部分处理
		}
    }
	for(int i=1;i<=w;i++)if(s[i]>ma)ma=s[i];
	printf("%d",ma);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值