题目大意: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 }
View Code

 

 posted on 2021-01-26 15:33  haianx  阅读(58)  评论(0编辑  收藏  举报