zoukankan      html  css  js  c++  java
  • Codeforces 1459D

    time limit per test 2 seconds
    memory limit per test 512 megabytes
    input standard input
    output standard output

    There are n glasses on the table numbered 1,…,n. The glass i can hold up to ai units of water, and currently contains bi units of water.

    You would like to choose k glasses and collect as much water in them as possible. To that effect you can pour water from one glass to another as many times as you like. However, because of the glasses’ awkward shape (and totally unrelated to your natural clumsiness), each time you try to transfer any amount of water, half of the amount is spilled on the floor.

    Formally, suppose a glass i currently contains ci units of water, and a glass j contains cj units of water. Suppose you try to transfer x units from glass i to glass j (naturally, x can not exceed ci). Then, x/2 units is spilled on the floor. After the transfer is done, the glass i will contain ci−x units, and the glass j will contain min(aj,cj+x/2) units (excess water that doesn’t fit in the glass is also spilled).

    Each time you transfer water, you can arbitrarlly choose from which glass i to which glass j to pour, and also the amount x transferred can be any positive real number.

    For each k=1,…,n, determine the largest possible total amount of water that can be collected in arbitrarily chosen k glasses after transferring water between glasses zero or more times.

    Input

    The first line contains a single integer n (1≤n≤100) — the number of glasses.

    The following n lines describe the glasses. The i-th of these lines contains two integers ai and bi (0≤bi≤ai≤100, ai>0) — capacity, and water amount currently contained for the glass i, respectively.

    Output

    Print n real numbers — the largest amount of water that can be collected in 1,…,n glasses respectively. Your answer will be accepted if each number is within 10−9 absolute or relative tolerance of the precise answer.

    Example
    input
    3
    6 5
    6 5
    10 2
    output
    7.0000000000 11.0000000000 12.0000000000

    Note

    In the sample case, you can act as follows:

    for k=1, transfer water from the first two glasses to the third one, spilling (5+5)/2=5 units and securing 2+(5 + 5)/2=7 units;
    for k=2, transfer water from the third glass to any of the first two, spilling 2/2=1 unit and securing 5+5+2/2=11 units;
    for k=3, do nothing. All 5+5+2=12 units are secured.

    题目大意:

    输入一个n,表示有n个瓶子。接下来有n行,每行第一个数代表该瓶子最大的容量,第二个数表示该瓶子当前的水量。你可以将一个瓶子的水倒入另一个瓶子中,假设到S的水,则另一个瓶子的当前容量应该为min(a[i], b[i] + s / 2),因为每次倒水都会损失一半,输出n个数,第 i 个数表示选其中的 i 个瓶子,这几个瓶子的含水量和最大是多少。

    解题思路:

    背包DP问题。每个瓶子有选和不选两种状态,其他瓶子可以往这几个瓶子里倒水。一维的状态可以表示为f[i][j][k],表示前i个瓶子,从里面选j个,容量恰好为k时(为什么用恰好而不是<=后面会有解释),水量的最大值是多少,在当前状态下,状态转移方程可以表示为:f[i][j][k] = f[i - 1][j - 1][k - a[i]] + b[i],当前状态只与上一层有关,所以可以优化掉一维,f[i][j] 表示前n个瓶子,选 i 个,当前容量为 j 时的水量最大值。就可以推出方程:f[i][j] = f[i - 1][j - a[i]] + b[i]
    这个问题其实可以抽象成二维费用的背包问题,第一维的费用永远是1,也就是个数,第二维的费用是容量,所以直接拿二维费用的板子写,因为这里用的是恰好,所以把f[0][0] 初始化为0,其他为 -INF 即可。
    为什么用恰好来表示状态,因为题目还有一个限制条件,输出选择 i 个的最大水量和,预处理一个A和B,表示容量总和和已有的水量总和,对于每个 i ,j从0 -> A, 每次res = max(res, min(j, f[i][j] + (B - f[i][j]) / 2)),整理一下,res = max(res, min(j, (B + f[i][j]) / 2)),然后每个 i 输出对应的res就好了。

    Code:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <map>
    #include <vector>
     
    using namespace std;
     
    typedef long long LL;
     
    const int N = 110;
     
    int n, A, B;
    int a[N], b[N];
    int f[N][N * N];
     
    int main() {
        scanf("%d", &n);
     
        for (int i = 1; i <= n; i ++) {
            scanf("%d%d", a + i, b + i);
            A += a[i], B += b[i];
        }
     
        memset(f, -0x3f, sizeof f);
        f[0][0] = 0;
     
        for (int i = 1; i <= n; i ++) {
            for (int j = i; j >= 1; j --)
                for (int k = A; k >= a[i]; k --)
                    f[j][k] = max(f[j][k], f[j - 1][k - a[i]] + b[i]);
        }
     
        for (int i = 1; i <= n; i ++) {
            double res = 0;
            for (int j = 0; j <= A; j ++) res = max(res, min(1.0 * j, 0.5 * (B + f[i][j])));
            printf("%.10lf ", res);
        }
     
        return 0;
    }
    
    
  • 相关阅读:
    MongoDB 部署复制集(副本集)
    MongoDB shell 2 副本集方法
    MongoDB shell 1 数据库方法
    MongoDB shell 0 集合方法
    CentOS7 安装 zabbix
    MongoDB Shell db.runCommand
    MongoDB Index
    MongoDB 启动报错
    MongoDB 聚合查询报错
    MongoDB 聚合函数
  • 原文地址:https://www.cnblogs.com/Hayasaka/p/14294119.html
Copyright © 2011-2022 走看看