Codeforces Round /#691 (Div. 2) D. Glass Half Spilled(DP,背包) 含两种写法


题目地址

https://codeforces.com/contest/1459/problem/D

题目大意

$n\leq 100$

有$n$个玻璃杯,有$a[i]$和$b[i]$两个属性,分别代表容量和里面已经有的水,转移水的时候会损失一半的水被撒到地上。

问把水集中到$k$个杯子里的时候,最多总共可以有多少水?$k=1,2,3,4\dots n$

解法

这题居然是一个几乎是裸的01背包

有两种写法,先讲我一开始写的一种:

是两个限制条件的01背包$dp[i][j][k]$,第一维是“当只考虑前i个杯子时”,第二维是“取了几个杯子”,第三维是这些杯子里的初始水量$b[i]$之和。

这时候就可以开始按照裸01背包写了,$dp[i][j][k]$维护这个条件下最大的容量!为什么是最大的容量呢?因为选中的$k$个杯子中初始水量和相等的情况下,肯定是容量越大最后的结果也会越大(或者相等,但肯定不会小)。

考虑到空间复杂度,$dp$数组需要把第一维变成滚动的来节省空间。

在处理完$dp[j][k]$数组之后,只需要按照题意列出实际值,比较后得出结果即可

实际值也就是 除去k个杯子外的总水量$\sum_{i=1}^{n} b[i]-k$除以2之后再加上k,即$k+(sum-k)/2$,这个值还要跟$dp[j][k]$取$min$,因为杯子的容量有限,最后就是$max(min(dp[j][k],k+(sum-k)/2))$。

——————————分割线———————————-

第二种方法就是k改成k个杯子的总容量

这个时候的结果就是$max(res,min(k,dp[j][k]+(sum-dp[j][k])/2))$

两个值得注意的点

1.因为有的杯子的初始水量为0,所以一开始开始$O(n^3)$求$dp[j][k]$的时候$k$也要从0开始。

2.因为有的杯子的初始水量为0,所以最后结果的循环要从0开始。

代码

解法1

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repb(i,a,b) for(int i=(a);i>=(b);i--)
const int MAXN = 114;
#define INF 0x3f3f3f3f
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
int dp[MAXN][10010];
int a[MAXN],b[MAXN];
int n,tot;
void solve(){
    cin>>n;
    tot = 0;
    rep(i,1,n){
        cin>>a[i]>>b[i];
        tot+=b[i];
    }
    memset(dp,-INF,sizeof(dp));
    dp[0][0]=0;
    rep(i,1,n){
        repb(j,i,1){
            repb(k,tot,0){//错在这里,因为可能有bi=0的情况
                if(k>=b[i]&&j) dp[j][k] = max(dp[j][k],dp[j-1][k-b[i]]+a[i]);
            }
        }
    }
    rep(j,1,n){
        double res = 0;
        rep(k,0,tot){//第二处错在这里,应该从0开始,因为k是水量不是容量,所以也可能为0
            res = max(res,min((double)dp[j][k],k+(double)(tot-k)/2));
        }
        cout<<res<<' ';
    }
    cout<<endl;
}
int main(){
    solve();
}

解法2

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repb(i,a,b) for(int i=(a);i>=(b);i--)
const int MAXN = 114;
#define INF 0x3f3f3f3f
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
int dp[MAXN][10010];
int a[MAXN],b[MAXN];
int n,tota,totb;
void solve(){
    cin>>n;
    tota = totb = 0;
    rep(i,1,n){
        cin>>a[i]>>b[i];
        tota+=a[i];
        totb+=b[i];
    }
    memset(dp,-INF,sizeof(dp));
    dp[0][0]=0;
    rep(i,1,n){
        repb(j,i,1){
            repb(k,tota,0){
                if(k>=a[i]&&j) dp[j][k] = max(dp[j][k],dp[j-1][k-a[i]]+b[i]);
            }
        }
    }
    rep(j,1,n){
        double res = 0;
        rep(k,0,tota){
            if(dp[j][k]>=0){
                res = max(res,min((double)k,(double)dp[j][k]+(double)(totb-dp[j][k])/2));
            }
        }
        cout<<res<<' ';
    }
    cout<<endl;
}
int main(){
    solve();
}

文章作者: REXWind
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 REXWind !
评论
  目录