CF730J 题解

原题链接-Bottles

思路

第一问

对于第一问,非常容易。设 sum=i=1naisum=\sum\limits^n_{i=1}a_i。只需要排序,然后找到最大的满足 j=1iajsum\sum\limits_{j=1}^ia_j\ge sumii,就是第一问的答案,记为 limitslimits

第二问

对于最优解问题,考虑 dp。

定义状态

有以下条件需要被满足:

  • nn 个水瓶进行考虑选择与否。

  • 选择的水瓶数量不能超过 limitslimits

  • 要有足够的容量装水。

其实也可以看做背包问题,价格为 bib_i,价值为 aia_i


我们就可以以此定义 dpi,j,kdp_{i,j,k} 为考虑了第 ii 个水瓶,选择了 jj 个,容量共有 kk 的最大 aia_i 之和。

为什么是最大值?因为 sumsum 一定。如果不用转移的水更多,要转移的水更少。

状态转移方程

根据背包问题的解法,对第 ii 个瓶子分类讨论。有选与不选(其实也就是 01 背包),即:

dpi,j,k=max{dpi1,j,k,dpi1,j1,kbi+ai}dp_{i,j,k}=\max\{dp_{i-1,j,k},dp_{i-1,j-1,k-b_i}+a_i\}

这样做还会爆空间,倒着枚举可以省掉 ii 这一维。

#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;
}
posted @ 2023-08-17 07:28  cjrqwq  阅读(15)  评论(0)    收藏  举报  来源