At332 E - Lucky bag

E.Lucky bag


原题链接

题意简述

\(n\) 件商品,\(m\) 件袋子,满足\(n \geq m\),要求分配没件商品到袋子里,袋子里可以没有物品或多个物品,但没件商品分配到一个袋子且仅分配一个袋子。求m个袋子中所有分配方案中物品最小化极差的极差

解题思路1

注意到方差的式子可以化简到\(V=\frac{D·\sum_{i=1}^D {x_i^2}+({\sum_{i=1}^D {x_i}})^2 }{D·D}\)
充分发挥人类智慧,(随机化算法+贪心思路)发现\(({\sum_{i=1}^D {x_i}})^2\)其实是定值序列和的平方,那么我们只需要在给定时间范围内求\(\sum_{i=1}^D {x_i^2}\),我们不断枚举排列顺序,贪心加入每个最小的袋子,计算最小化\(\sum_{i=1}^D {x_i^2}\)
最多进行4e61515=9e8次操作,对atcoder神机2s时限的情况下完全不成问题
使用std::shuffle枚举加入顺序

AC code1

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n,m;cin>>n>>m;
    vector<int>a(n);
    ll sum=0;
    for(auto &x:a) cin>>x,sum+=x;
    mt19937 rnd(0xab8d5f6);
    ll minl=4e18;
    auto check=[&]() ->ll {
        vector<ll>b(m);
        for(auto &x:a) *min_element(b.begin(),b.end())+=x;
        ll ans=0;
        for(auto &x:b) ans+=x*x;
        return ans;
    };
    for(int i=0;i<=4e6+10;i++){
        shuffle(a.begin(),a.end(),rnd);
        minl=min(minl,check());
    }
    cout<<fixed<<setprecision(15)<<(minl*m-sum*sum)*1.00/(m*m)<<endl;
    return 0;
}

解题思路2

状压dp,考虑枚举前i个袋子已经的状态j选择的最小平方和,转移方程为
$dp_{i,j}=min(dp_{i,j},dp_{i-1,k}+dp_{1,k\oplus j})(k \subseteq j) $,单独考虑每个物品时,考虑有三种状态,单独在 \(j\) 中存在,在 \(j\)\(k\) 中存在,不在 \(j\) 也不在 \(k\) 中存在,所以物品共有 \(3^n\) 中状态,合计复杂度 \(O(D·3^n)\)

AC code2

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int n,d;cin>>n>>d;
    vector<ll>s(1<<n|1);
    for(int i=0,x;i<n;i++){
        cin>>x;
        for(int j=0;j<(1<<n);j++){
            if(j>>i&1) s[j]+=x;
        }
    }
    for(int i=0;i<(1<<n);i++) s[i]*=s[i];
    vector<vector<ll> >f(d+1,vector<ll>(1<<n|1));
    for(int i=1;i<(1<<n);i++) f[0][i]=1e18;
    for(int i=1;i<=d;i++){
        for(int j=0;j<(1<<n);j++){
            f[i][j]=s[j];
            for(int k=j;k;k=(j&k-1)){
                f[i][j]=min(f[i][j],f[i-1][k]+s[j^k]);
            }
        }
    }
    cout<<fixed<<setprecision(15)<<1.0L*(f[d][(1<<n)-1]*d-s[(1<<n)-1])/(d*d);
    cout<<endl;
    return 0;
}
posted @ 2025-05-21 22:12  usedchang  阅读(13)  评论(0)    收藏  举报