E - Lucky bag

E - Lucky bag

Problem Statement

AtCoder Inc. sells merchandise on its online shop.

There are N items remaining in the company. The weight of the i-th item (1iN) is Wi.

Takahashi will sell these items as D lucky bags.
He wants to minimize the variance of the total weights of the items in the lucky bags.
Here, the variance is defined as V=1Di=1D(xix¯)2, where x1,x2,,xD are the total weights of the items in the lucky bags, and x¯=1D(x1+x2++xD) is the average of x1,x2,,xD.

Find the variance of the total weights of the items in the lucky bags when the items are divided to minimize this value.
It is acceptable to have empty lucky bags (in which case the total weight of the items in that bag is defined as 0),
but each item must be in exactly one of the D lucky bags.

Constraints

  • 2DN15
  • 1Wi108
  • All input values are integers.

Input

The input is given from Standard Input in the following format:

N D
W1 W2 WN

Output

Print the variance of the total weights of the items in the lucky bags when the items are divided to minimize this value.
Your output will be considered correct if the absolute or relative error from the true value is at most 106.


Sample Input 1

5 3
3 5 3 6 3

Sample Output 1

0.888888888888889

If you put the first and third items in the first lucky bag, the second and fifth items in the second lucky bag, and the fourth item in the third lucky bag, the total weight of the items in the bags are 6, 8, and 6, respectively.

Then, the average weight is 13(6+8+6)=203,
and the variance is 13{(6203)2+(8203)2+(6203)2}=89=0.888888, which is the minimum.

Note that multiple items may have the same weight, and that each item must be in one of the lucky bags.

 

解题思路

  为了方便这里用 m 来表示题目中的 D,且物品和分组的下标都从 0 开始。

  容易知道 x¯=1mi=0m1xi=1mi=0n1wi,因此 x¯ 是一个常量。所以要使得式子 1mi=0m1(xix¯)2 尽可能小,只需让 i=0m1(xix¯)2 尽可能小。因此实际上要考虑的是每组应该放哪些物品才能使得式子尽可能小。

  考虑动态规划,定义 f(i,j) 表示已将状态 j 所表示的物品放入前 i 组的所有方案中 k=0i(xkx¯)2 的最小值,其中 j 是一个 n 位的二进制数,如果第 k 位是 1 表示已选第 k 个物品。为了方便这里定义 sj 表示状态 j 中已选物品的总价值,根据第 i 组含有哪些物品进行状态划分,因此有状态转移方程 {f(i,j)=(sjx¯)2,i=0f(i,j)=minkj{f(i1,jk)+(skx¯)2},i>0

  其中 k 也是二进制状态,表示 j 关于 1 的子集。例如 (101)2,那么其子集就是 (101)2(100)2(001)2(000)2。用 popcount(j) 表示二进制数 j1 的数量,那么 j 的子集数量就是 2popcount(j)。遍历 j 的所有子集的方法和证明请参考链接

  整个 dp 的时间复杂度是 O(mi=0n2iCni)=O(m3n)。其中 i=0n2iCni 可由 (1+2)n 的二项式定理推出。计算量上限大概是 2×108 级别,在 atcoder 上 300ms 左右就可以跑出结果。

  最终答案就是 1mf(m1,2n1)

  AC 代码如下:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 15, M = 1 << 15;

int w[N], s[M];
double f[N][M];

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%d", w + i);
    }
    for (int i = 0; i < 1 << n; i++) {
        for (int j = 0; j < n; j++) {
            if (i >> j & 1) s[i] += w[j];
        }
    }
    double avg = 1.0 * s[(1 << n) - 1] / m;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < 1 << n; j++) {
            if (!i) {
                f[i][j] = (s[j] - avg) * (s[j] - avg);
            }
            else {
                f[i][j] = f[i - 1][j] + avg * avg;
                for (int k = j; k; k = (k - 1) & j) {
                    f[i][j] = min(f[i][j], f[i - 1][j ^ k] + (s[k] - avg) * (s[k] - avg));
                }
            }
        }
    }
    printf("%.10f", f[m - 1][(1 << n) - 1] / m);
    
    return 0;
}

 

参考资料

  Editorial - AtCoder Beginner Contest 332:https://atcoder.jp/contests/abc332/editorial/7929

posted @   onlyblues  阅读(117)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示