状态压缩,dp
代码如下:注释写的很详细了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF= 0x3f3f3f3f;
const int N=3e5+5e4;
int n,m;
double w[20];
double s[N];
double dp[20][N];
void solved(){
double avg =s[(1<<n)-1]/m; //每个袋子的平均权重
for(int i=0;i<m;i++){
for(int j=(1<<n)-1;j>=0;j--){
if(i==0){
dp[i][j]=(s[j]-avg)*(s[j]-avg); //第一个幸运袋,直接放入
}else{
dp[i][j]=dp[i-1][j]+avg*avg; //先假设当前袋子不放东西(需加上(avg-0)^2)
/*当用比特序列表示集合时,可以从
k=j 开始,反复用 (k−1)&j 替换 k 直到 k=0 来枚举与比特序列
j 相对应的集合子集。(变量 k 跨越了与 j 所有子集相对应的比特序列)*/
for(int k=j;k;k=(k-1)&j){ //枚举子集
/*状态转移方程式*/
/*也可以是dp[i][j]=min(dp[i][j],dp[i-1][j^k]+(s[k]-avg)*(s[k]-avg)); */
dp[i][j]=min(dp[i][j],dp[i-1][j-k]+(s[k]-avg)*(s[k]-avg)); //dp[0][k]可换为(s[k]-avg)^2
}
}
}
}
cout<<fixed<<setprecision(15)<<dp[m-1][(1<<n)-1]/m<<endl;
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++) cin>>w[i];
for(int i=(1<<n)-1;i>=0;i--){ //有2^n个子集
for(int j=0;j<n;j++){
if((i>>j)&1){
s[i]+=w[j]; //s[i]表示i的二进制中第j位是否放物品之和
}
}
}
//然后使用dp[i][j]表示0~i的袋子中放入了方案包含的物品的最小方差
solved();
return 0;
}