传送门
题目描述
Nick has n bottles of soda left after his birthday. Each bottle is described by two values: remaining amount of soda ai and bottle volume bi (ai?≤?bi).
Nick has decided to pour all remaining soda into minimal number of bottles, moreover he has to do it as soon as possible. Nick spends x seconds to pour x units of soda from one bottle to another.
Nick asks you to help him to determine k — the minimal number of bottles to store all remaining soda and t — the minimal time to pour soda into k bottles. A bottle can‘t store more soda than its volume. All remaining soda should be saved.
思路
首先发现最开始的一步肯定是贪心,按容量sort一遍可以得到第一个答案,然后稍微转化一下题意,就可以发现,转化后的提议是这个样子:从n个瓶子里面选出num个,使得这num个瓶子的容积之和要大于水的体积,并且当时间最短时,一定有这num个瓶子里的水体积和最大,就可以发现是一个01背包问题。
dp[i][j]表示选了i个瓶子,一共的容积为j的时候,这i个瓶子里水的最大值。由此可得dp方程
dp[j][k]=max(dp[j][k],dp[j-1][k-b[i].v]+b[i].r);
小细节
差点被坑死在这里,按照容量枚举k的时候一定要倒着枚举,不然就成完全背包了2333333。
而且要注意枚举的顺序,最外层枚举的是选第i个,然后枚举容量,然后在枚举选了多少个,这样可以保证一个瓶子只选一次,不然会挂得很惨qwq
代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct nod{ int r; int v; }; nod b[105]; inline int cmp(nod x,nod y){ return x.v>y.v; } int dp[105][20000]; int main(){ int n; cin>>n; int rest=0; for(register int i=1;i<=n;i++){ cin>>b[i].r; rest+=b[i].r; } for(register int i=1;i<=n;i++){ cin>>b[i].v; } sort(b+1,b+n+1,cmp); memset(dp,-1,sizeof(dp)); dp[0][0]=0; int num=-1,tot=0; for(register int i=1;i<=n;i++){ tot+=b[i].v; if(tot>=rest&&num==-1)num=i; } cout<<num<<endl; for(register int i=1;i<=n;i++){ //xuan di ji ge for(register int k=tot;k>=b[i].v;k--){ for(register int j=1;j<=num;j++){ //xuan le ji ge if(dp[j-1][k-b[i].v]!=-1){ dp[j][k]=max(dp[j][k],dp[j-1][k-b[i].v]+b[i].r); //cout<<dp[j-1][k-b[i].v]<<‘ ‘<<b[i].r<<endl; //cout<<i<<‘ ‘<<j<<‘ ‘<<k<<endl<<endl; } } } } // while(1){ // int x,y; // cin>>x>>y; // cout<<dp[x][y]<<endl; // } int ans=-1; for(register int i=rest;i<=tot;i++){ ans=max(ans,dp[num][i]); } //cout<<tot<<endl; cout<<rest-ans<<endl; }