Description
n种点心,每种点心有一定的能量t,体积u和数量v,现在要用m种卡车搬运这些点心,每种开车有一定的容量x,花费y和数量z,点心可以分割但选一块就要都选,现在问将运送总能量不小于p的点心的最小花费
Input
第一行一整数T表示用例组数,每组用例首先输入三个整数n,m,p分别表示点心种类数,车的种类数以及目标能量值,之后n行每行三个整数ti,ui,vi表示第i种点心你的能量值,体积和数量,之后m行每行三个整数xi,yi,zi表示第i种卡车的容量,花费和数量
(T<=10,1<=n,m<=200,0<=p<=50000,1<=ti,ui,vi,xi,yi,zi<=100)
Output
对于每次用例,问运送总能量不小于p的点心所需的最小花费,如果没有合法运送方案或者最小花费大于50000则输出TAT
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
Solution
做两遍多重背包,第一遍对点心,求出总能量不小于p的最小体积minv,第二遍对卡车,求出总容积不小于minv的最小花费minc,第一遍背包上限p+100,第二遍背包上限50000,复杂度可以接受
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 55555
int T,n,m,p,t[222],u[222],v[222],x[222],y[222],z[222],dp[maxn];
int res,a[maxn],b[maxn];
int Solve(int n,int v[],int w[],int num[])
{
int sum=0;
for(int i=1;i<=n;i++)sum+=num[i]*v[i];
res=0;
for(int i=1;i<=n;i++)
{
int k=1;
while(k<num[i])
{
a[res]=k*v[i],b[res++]=k*w[i];
num[i]-=k;
k*=2;
}
if(num[i])a[res]=num[i]*v[i],b[res++]=num[i]*w[i];
}
return sum;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++)scanf("%d%d%d",&t[i],&u[i],&v[i]);
for(int i=1;i<=m;i++)scanf("%d%d%d",&x[i],&y[i],&z[i]);
int temp=Solve(n,t,u,v);
if(temp<p)printf("TAT\n");
else
{
int minv=INF;
for(int i=1;i<maxn;i++)dp[i]=INF;
dp[0]=0;
//dp[j]表示前i个物品总能量为j所需要的最小体积
for(int i=0;i<res;i++)
for(int j=p+100;j>=a[i];j--)
{
dp[j]=min(dp[j],dp[j-a[i]]+b[i]);
if(j>=p)minv=min(minv,dp[j]);
}
temp=Solve(m,x,y,z);
if(temp<minv)printf("TAT\n");
else
{
memset(dp,0,sizeof(dp));
int minc=INF;
//dp[j]表示前i辆卡车花费为j的最大容积
for(int i=0;i<res;i++)
for(int j=50000;j>=b[i];j--)
{
dp[j]=max(dp[j],dp[j-b[i]]+a[i]);
if(dp[j]>=minv)minc=min(minc,j);
}
if(minc>50000)printf("TAT\n");
else printf("%d\n",minc);
}
}
}
return 0;
}