dp day2-背包

混合背包

分别处理就好啦

二维费用的背包问题

要多一维数组,其余和前三种基本一样

洛谷p1509找GF(tan90)

除了二维以外还有一个最大数量时求最小花费的限制,先是用结构体写了一个最简陋的版本(然后发现题解也没有更优化的版本......)

把妹子看成有两种花费(money rp)的物品(这样会被打的吧),然后dp

#include<bits/stdc++.h>
using namespace std;
struct girl
{
	int money,rp,time;
};
girl g[1010];
struct vir
{
	int num,cost;
};
vir s[1010][1010];
int n,m,r;
void init()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d%d%d",&g[i].money,&g[i].rp,&g[i].time);
	scanf("%d%d",&m,&r);
	return;
}

bool check(int i,int j,int k)
{
	if(s[i-g[k].money][j-g[k].rp].num+1>s[i][j].num)return true;//如果选上后人数更多
	if(s[i-g[k].money][j-g[k].rp].num+1==s[i][j].num&&
	s[i-g[k].money][j-g[k].rp].cost+g[k].time<s[i][j].cost)return true;//如果选上后人数一样但是时间更少
	return false;
}

void dp()
{
	for(int i=0;i<=m;i++)
	for(int j=0;j<=r;j++)
	s[i][j].num=0,s[i][j].cost=0;
	for(int k=1;k<=n;k++)//两个费用的01背包
	for(int i=m;i>=g[k].money;i--)
	for(int j=r;j>=g[k].rp;j--)
	if(check(i,j,k))//如果选这个妹子更优
	{
	    s[i][j].num=s[i-g[k].money][j-g[k].rp].num+1;
            s[i][j].cost=s[i-g[k].money][j-g[k].rp].cost+g[k].time;
	}
	return;
}
	
void print()
{
	if(s[m][r].num==0)//OIer是没有妹子的哈哈哈哈哈哈哈嗝
	{
		printf("0");
		return;
	}
	printf("%d",s[m][r].cost);
	return;
}

int main()
{
	init();
	dp();
	print();
	return 0;
}

分组背包

洛谷p1757通天之分组背包

具体看代码

#include<bits/stdc++.h>
using namespace std;
int m,n,s[1010]={0},temp=0;
struct subject
{
	int weight,value,team;
};
subject a[1010];
int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++)
	{
	    scanf("%d%d%d",&a[i].weight,&a[i].value,&a[i].team);
	    if(a[i].team>temp)temp=a[i].team;
	}
	for(int t=1;t<=temp;t++)//组别在前保证每一组都分别被计算,不同组互不影响
	for(int i=m;i>0;i--)//容量,倒序保证01背包
	for(int j=1;j<=n;j++)//容量在前保证该容量尝试了该组中的每一个物品且没有选择多个物品
	if(a[j].team==t&&i-a[j].weight>=0)
	s[i]=max(s[i],s[i-a[j].weight]+a[j].value);
	printf("%d",s[m]);
	return 0;
}
有依赖的背包

洛谷p1064金明的预算方案

可以简单模拟选取方案,01背包过(pascal时期也是这么写的),但是为了严谨,还是敲了这个出来:

#include<bits/stdc++.h>
using namespace std;
int n,m,s[40100]={0},t[40100]={0};
struct subject
{
	int weight,value,team;
};
subject a[70];
void init()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i].weight,&a[i].value,&a[i].team);
		a[i].value*=a[i].weight;
	}
	return;
}

void dp()
{
	for(int i=1;i<=m;i++)
	if(a[i].team==0)//如果是主物品 
	{
		for(int j=1;j<=a[i].weight;j++)t[j]=0;//t数组存这组物品选择后可达到的最大值 
		for(int j=a[i].weight;j<=n;j++)t[j]=s[j-a[i].weight]+a[i].value;//先要全选k物品 
		for(int j=1;j<=m;j++)
		  if(a[j].team==i)//如果是k这一组
		    for(int k=n;k>=a[i].weight+a[j].weight;k--)//注意下界 
		      t[k]=max(t[k],t[k-a[j].weight]+a[j].value);//在a的基础上01背包 
		for(int j=a[i].weight;j<=n;j++)//如果该组选择后比当前值大 
		if(t[j]>s[j])s[j]=t[j];
	}
	return;
}

int main()
{
	init();
	dp();
	printf("%d",s[n]);
	return 0;
}
ps:修改程序要前后一致,脑子发涨造成for(int j=a[i].weight;j<=n;i++),卡了好一会儿......

多人背包

洛谷p1858多人背包

需要多一维来存储前k个最优解的序列,与01背包相似

#include<bits/stdc++.h>
using namespace std;
int k,v,n,s[5010][60],ans=0,temp[60];
struct subject
{
	int w,val;
};
subject a[110];
void init()
{
    scanf("%d%d%d",&k,&v,&n);
	for(int i=1;i<=n;i++)
	scanf("%d%d",&a[i].w,&a[i].val);
	return;
}
	
void dp()
{
	for(int i=1;i<=n;i++)//真-01背包模版
	for(int j=v;j>=a[i].w;j--)
	{
            int x=1,y=1,t=1;
	    while(t<=k)//选取k个最优解,temp存储防止覆盖
	    {
		if(s[j][x]>=s[j-a[i].w][y]+a[i].val)
	    	temp[t]=s[j][x],x++;
	    	else temp[t]=s[j-a[i].w][y]+a[i].val,y++;
	    	t++;
	    }
	for(int x=1;x<=k;x++)s[j][x]=temp[x];
    }
	return;
}

void print()
{
	for(int i=1;i<=k;i++)
	ans+=s[v][i];
	printf("%d",ans);
	return;
}

int main()
{
	init();
	for(int i=0;i<=v;i++)
	for(int j=0;j<=k;j++)s[i][j]=-1000000;//该题要求装满,所以初始化如此
	s[0][1]=0;
	dp();
	print();
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值