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;
}

浙公网安备 33010602011771号