4224. 【五校联考3day1】食物

Description

Input

Output

Sample Input

4
1 1 7
14 2 1
1 2 2
1 1 10
10 10 1
5 7 2
5 3 34
1 4 1
9 4 2
5 3 3
1 3 3
5 3 2
3 4 5
6 7 5
5 3 8
1 1 1
1 2 1
1 1 1

Sample Output

4
14
12
TAT

Data Constraint

Solution

        多重背包问题。

        因为食物可以被分开运输,所以只要运输的总空间大于美味值大于等于p的所有食物所需要的空间之和即可。可以将问题分成两个部分:

  • 选出美味度之和不小于p的食物并且要求空间最小
  • 选出空间之和不小于第一步的结果并且费用尽可能的少。

        第一步我们可以用多重背包轻松解决,但第一步的答案可能会很大,无法在第二问直接用背包。不妨将状态和结果互换,以需要的费用作为状态,而用最大的空间作为答案。至此,我们可以设f[i]表示美味度之和为i的食物所用的最少空间,g[i]表示费用为i时最大能装的空间。注意到最大的费用只有50000,所以是可行的。但是需要注意的一点是,当求f[i]时,因为p<=50000所以我们可能会以为最大的食物的美味值之和只用做到50000,但实际上要做到更大一点,因为有可能几个食物的美味值大于了50000,但空间却更小,所以我们就要做到50000再多加一个食物的美味度就够了(即做到的当前这个),因为若多选2个或以上虽然美味值增加了,但是空间一定会比选一个的要少。

        最后,我们发现时间复杂度乘了一个test后,可能会很大,所以要加二进制拆分法或是单调队列优化多重背包,详细内容可自行在网上搜索或查看我的博客。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2100
using namespace std;
int T,n,m,p,t,u,v,x,y,z,st,tot,ans,a[N],b[N],f[50110],g[50010];
int read(){
	int w=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return w*x;
}
void add(int x,int y,int z){//二进制拆分法优化
	st=1;tot=0;
	while(z>=st){
		a[++tot]=st*x;
		b[tot]=st*y;
		z-=st;st<<=1;
		if(z&&st>z) st=z;
	}
}
int main(){
	freopen("food.in","r",stdin);
	freopen("food.out","w",stdout);
	T=read();
	while(T--){
		memset(f,127,sizeof(f));
		memset(g,0,sizeof(g));
		f[0]=0;
		n=read(),m=read(),p=read();
		for(int i=1;i<=n;i++){
			t=read(),u=read(),v=read();
			add(t,u,v);
			for(int j=1;j<=tot;j++)
				for(int k=p+a[j];k>=a[j];k--) f[k]=f[k]>f[k-a[j]]+b[j]?f[k-a[j]]+b[j]:f[k];
			/*for(int j=1;j<=v;j++)
				for(int k=50000;k>=t;k--) 
					f[k]=min(f[k],f[k-t]+u);*/
                    //不加优化的多重背包
		}
		u=ans=50001;
		for(int i=p;i<=p+100;i++) u=u>f[i]?f[i]:u;
		if(u>50000){
			printf("TAT\n");
			for(int j=1;j<=m;j++) x=read(),y=read(),z=read();
			continue;
		}
		for(int i=1;i<=m;i++){
			x=read(),y=read(),z=read();
			add(x,y,z);
			for(int j=1;j<=tot;j++)
				for(int k=ans;k>=b[j];k--){//比ans大的肯定更新不了答案,所以可以直接从ans开始枚举
					g[k]=g[k-b[j]]+a[j]>g[k]?g[k-b[j]]+a[j]:g[k];
					if(g[k]>=u) ans=k;
				}	
			/*for(int j=1;j<=z;j++)
				for(int k=50000;k>=y;k--){
					g[k]=max(g[k],g[k-y]+x);
					if(g[k]>=u) ans=k;
				}*/
                    //不加优化的多重背包
		}
		printf(ans==50001?"TAT\n":"%d\n",ans);
	}
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/86669291

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值