题目链接:https://vjudge.net/contest/373875#problem/J
题意:给n个瓶子,每个瓶子放了一些水,现在要把这些水尽可能汇总到最少的瓶子了,问最少的瓶子数与移到这些瓶子里所要的最小代价
解题思路:
最少瓶子数很好求,直接先把水的总量rsum求出来,然后从大的容量的瓶子开始放,就能得到最少瓶子数k
求最小的代价,就相当于从总的瓶子里面找出k个能放入rsum水的瓶子,并且这些瓶子中本来的水的量最多
利用0/1背包,dp[i][j]表示容量为i、选取了j个物品的最大价值
将n个瓶子看成n个物品,每个物品价值为瓶子中的水bot[i].r,占用空间为bot[i].v,求取k个瓶子占用空间大于等于rsum的最大价值。
0/1背包部分我花了好长时间才写出来,一直在想该怎么处理选择k个物品,哭了
DP主体:
void DP(int index,int vsum)
{
for(int i=0;i<=vsum;i++)
for(int j=0;j<=k;j++)
dp[i][j]=-99999999;
dp[0][0]=0;
for(int i=1;i<=index;i++){
for(int j=vsum;j>=bot[i].v;j--){
for(int p=k;p>=1;p--){
dp[j][p]=max(dp[j][p],dp[j-bot[i].v][p-1]+bot[i].r);
}
}
}
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int rsum;
int vsum;
int k;
int dp[11000][110];
struct node{
int v;
int r;
}bot[110];
bool cmp(node a,node b)
{
return (a.v==b.v)?(a.r>b.r):(a.v>b.v);
}
void DP(int index,int vsum)
{
for(int i=0;i<=vsum;i++)
for(int j=0;j<=k;j++)
dp[i][j]=-99999999;
dp[0][0]=0;
for(int i=1;i<=index;i++){
for(int j=vsum;j>=bot[i].v;j--){
for(int p=k;p>=1;p--){
dp[j][p]=max(dp[j][p],dp[j-bot[i].v][p-1]+bot[i].r);
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&bot[i].r),rsum+=bot[i].r;
for(int i=1;i<=n;i++) scanf("%d",&bot[i].v),vsum+=bot[i].v;
sort(bot+1,bot+1+n,cmp);
int tmp=rsum;
int res=-999999;
int index=0;
for(int i=1;i<=n;i++){
if(tmp>0)
k++,tmp-=bot[i].v;
else{
res=tmp+bot[i-1].v;
break;
}
}
int t=rsum;
index=k;
for(int i=k;i<=n;i++)
if(res<=bot[i].v){
index=i;
}
else
break;
DP(index,vsum);
int maxs=0;
for(int i=rsum;i<=vsum;i++)
maxs=max(maxs,dp[i][k]);
t-=maxs;
printf("%d %d\n",k,t);
return 0;
}