思路:这题是回溯枚举二叉树找最大值 有意思的是他是从叶开始构造,2个合成一个然后把新的加入子叶表里面 用index动态表示下表 vis[]表示存在与否 其中宽度的计算是难点 要考虑右边的子树的左儿子可能超过左边的子树的右儿子的类似情况,同时需要用数学推导出的公式计算新产生的合成节点的左右长度 由题意不用考虑精度,不然就坑爹了
#include<bits/stdc++.h>
using namespace std;
double r;
int s;
int vis[25];
double lw[25],rw[25];
int w[25];
int idex;
double ans=-1;
void dfs(int dep){
if(dep==s)return ; //递归s-1次表示已经全部用完了 到s直接可以停了
for(int i=0;i<12;i++)
if(vis[i]){
for(int j=0;j<12;j++){ //i左j右
if(vis[j]&&i!=j){
double R=max(rw[i]-1,rw[j]);double L=max(lw[i],lw[j]-1); //左边的到右边所以要减1右边同理
if(R+L+1<r){
if(dep==s-1){
ans=max(1+L+R,ans);
}
vis[j]=vis[i]=0;
vis[idex]=1; //这是新产生的树 单个点没有左右长度,而新产生的有,需要计算
w[idex] = w[i] + w[j];
lw[idex] = w[j] * 1.0 / w[idex] + L;
rw[idex] = w[i] * 1.0 / w[idex] + R;
idex++;
dfs(dep+1);
vis[--idex]=0;
vis[j]=vis[i]=1;
}
}
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
ans=-1;
idex=0;
memset(vis,0,sizeof(vis));
memset(rw,0,sizeof(rw));
memset(lw,0,sizeof(lw));
scanf("%lf%d",&r,&s);
for(int i=0;i<s;i++){scanf("%d",&w[i]);
vis[idex++]=1;
}
if(s==1){printf("0\n");continue;}
dfs(1);
printf("%.16f\n",ans);
}
return 0;
}