CF730J 题解
思路
第一问
对于第一问,非常容易。设 。只需要排序,然后找到最大的满足 的 ,就是第一问的答案,记为 。
第二问
对于最优解问题,考虑 dp。
定义状态
有以下条件需要被满足:
-
对 个水瓶进行考虑选择与否。
-
选择的水瓶数量不能超过 。
-
要有足够的容量装水。
其实也可以看做背包问题,价格为 ,价值为 。
我们就可以以此定义 为考虑了第 个水瓶,选择了 个,容量共有 的最大 之和。
为什么是最大值?因为 一定。如果不用转移的水更多,要转移的水更少。
状态转移方程
根据背包问题的解法,对第 个瓶子分类讨论。有选与不选(其实也就是 01 背包),即:
这样做还会爆空间,倒着枚举可以省掉 这一维。
#include<bits/stdc++.h>
#define b(__) b[mp[__]]
using namespace std;
const int N=1e2+1;
int n,a[N],ans=1e9,b[N],mp[N],sum,limits=1,S;
int dp[N][N*N];//第 i 个盘子,选 j 个 容量为 k
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i],mp[i]=i;
for(int i=1;i<=n;i++) cin>>b[i],S+=b[i];
sort(mp+1,mp+n+1,[](int x,int y) {return b[x]>b[y];});
for(int i=1,t=0;i<=n;i++) {
t+=b(i);//相当于排序的 b[i]
if(t>=sum) {
limits=i;
break;
}
}
memset(dp,-0x3f,sizeof dp);
dp[0][0]=0;//赋初始值
for(int i=1;i<=n;i++)
for(int j=min(limits,i);j>=1;j--)
for(int k=S;k>=b[i];k--)//此处 a,b 排序与否均可
dp[j][k]=max(dp[j][k],dp[j-1][k-b[i]]+a[i]);
for(int i=sum;i<=S;i++) ans=min(sum-dp[limits][i],ans);
//此时要有合法的状态!若 i<sum,说明装不下
cout<<limits<<" "<<ans<<"\n";
return 0;
}

浙公网安备 33010602011771号