题目大意:n个容器,每个容器有一定的容量,每个容器当前有水,容器之间可以倒水(任意),每次倒水,水会洒出来一半,问选择1-n个容器最多能得到多少水
解题思路:我们观察到数据范围是比较小的,往dp上靠一下。dp[i][j][k]表示前i个容器中选择j个容量为k时,这些所选容器内的最大初始水量。当我们得到最大初始水量后,我们知道总水量和容量
那么max((sum-dp[][][])/2+dp[][][],k)就是我们的答案,也就是max(sum+dp[][][],2*k)/2
我们在dp时前i个容器的容量是知道的,那么容量k就是0-sum[i]的,表示前i个选择j个容量为k时初始水量最大值
转移方程很容易写出来:dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-a[i]]+b[i])
用人话来说就是,前i个容器选j个容量为k时的最大初始水量是,不选择或者选择第i个容器,要选择第i个容器并且保证总共选择j个那么,就要从dp[i-1][j-1][k-a[i]]转移过来
事实上我们知道当前状态的i仅仅与当前状态或上一个状态(i-1)有关,那么滚动数组减少一维
最后找答案的时候只要在最后的dp中枚举j和k得到最大值就可以了
1 #include<stdio.h> 2 #include<algorithm> 3 #include<math.h> 4 #include<string.h> 5 using namespace std; 6 typedef long long ll; 7 const int maxn=2e5+5; 8 9 int n,m,t; 10 int a[maxn],b[maxn]; 11 int dp[2][110][10010]; 12 int suma[maxn],sumb; 13 int main(){ 14 scanf("%d",&n); 15 for(int i=1;i<=n;i++){ 16 scanf("%d%d",&a[i],&b[i]); 17 suma[i]=suma[i-1]+a[i]; 18 sumb+=b[i]; 19 } 20 int now=0; 21 memset(dp,-0x3f,sizeof(dp)); 22 dp[0][0][0]=0; 23 for(int i=1;i<=n;i++){ 24 now^=1; 25 for(int j=0;j<=i;j++){ 26 for(int k=0;k<=suma[i];k++){ 27 dp[now][j][k]=dp[now^1][j][k]; 28 } 29 } 30 for(int j=1;j<=i;j++){ 31 for(int k=a[i];k<=suma[i];k++){ 32 dp[now][j][k]=max(dp[now][j][k],dp[now^1][j-1][k-a[i]]+b[i]); 33 } 34 } 35 } 36 int ans=0; 37 for(int j=1;j<=n;j++){ 38 ans=0; 39 for(int k=0;k<=suma[n];k++){ 40 ans=max(ans,min(dp[now][j][k]+sumb,2*k)); 41 } 42 printf("%.10f%c",1.0*ans/2,j==n?'\n':' '); 43 } 44 return 0; 45 }