题意
给出 n n n 数,可以执行 k k k 次操作,每次从剩余的球里选一个,保证左边的数的数量是右边的倍数,求最大拿到的数的和。
思路
由于 n n n 很小,可以考虑压装。 d p x dp_x dpx 表示当前选择情况为 x x x 的最大权值。其中, x x x 的二进制位表示它对应的数是否被选择。
复杂度 O ( n 2 n ) \mathcal{O}(n2^n) O(n2n)。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
int n,k,a[21],dp[2000000],mx;
int main(){
//读入
cin>>n>>k,memset(dp,-1,sizeof dp),dp[0]=0;
for(int i=1;i<=n;i++) cin>>a[i];
//枚举x
for(int x=0;x<=(1<<n)-1;mx=max(mx,dp[x]),x++)
if(int co=__builtin_popcount(x);dp[x]!=-1&&co<k){
//判断是否能继续扩展
for(int i=1,l=0,r;i<=n;i++)//扩展
if(!(x&(1<<(i-1)))){//这个数没有被选上
r=n-co-l-1;//计算右边的数的个数
//满足要求就更新最大值
if(r&&!(l%r)) dp[x|(1<<(i-1))]=max(dp[x|(1<<(i-1))],dp[x]+a[i]);
l++;//左边的数字增加
}
}
cout<<mx;//输出最大值
return 0;
}