参考:https://www.jianshu.com/p/88e591c5dc0e
主要思想:枚举二叉树+计算宽度
如果只有1个叶子,直接输出宽度为0;否则,从第2个(一定有根节点-1)开始一层一层挨个枚举编号为num的节点的值,直到用完所有w。
计算宽度时,从最后一个填入的节点往前计算以它为中心的左右的长度,左边为负,右边为正,一直算到根节点(num = 1),R[1]-L[1]就是整个二叉树的宽度。
比如:
2号节点(-1)的左边宽度为-2,右边为+1。2号节点的子节点都是叶子(左右宽度都为0)所以简单,1号节点就有点复杂了,还有考虑孩子节点。
1号节点的左宽度:0 +(-4) 和 -3+2 中较小的那个。
1号节点的右宽度:0 - 4 和 1+2 中较大的那个。
总结:
L[i] = min( L[x]-l, L[y]+r );
R[i] = max( R[x]-l, R[y]+r );
其中x,y是 i 的左右孩子节点,l是 i 为中心的天平左臂长,r是右臂长。
#include <cstdio>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1005;
int t[N]; // 二叉树数组
double L[N],R[N],val[N],w[N];
double ans,wide;
bool vis[15];
int T,s;
// 计算二叉树宽度
void judge( int n ) {
memset( L,0,sizeof(L) );
memset( R,0,sizeof(R) );
memset( val,0,sizeof(val) );
for( int i=n; i>0; --i ) {
if( t[i]==-1 ) {
int x = 2*i;
int y = 2*i+1;
val[i] = val[x]+val[y];
double l = val[y]/val[i];
double r = val[x]/val[i];
L[i] = min( L[x]-l, L[y]+r );
R[i] = max( R[x]-l, R[y]+r );
}
else if( t[i] ){
val[i] = w[t[i]];
}
}
double a = R[1] - L[1];
if( a-wide<1e-5 ) {
ans = max( ans, a );
}
}
//使用数组t [1....num] 来模拟二叉树 根据二叉树的性质 如果parent = 节点 i 那么left child = 2*i right child = 2*i+1
// num : 当前正在枚举的位置 sit: 当前还剩下多少叶子节点可以填写 use: 当前还需要多少填写的内容
// -1 表示 该节点为悬挂点 不可填写内容但儿子可以填写内容
//0 表示 该节点不填写任何内容 也就是不表示出来 作为终止
//n 表示把第n个物体填写在这个位置
void dfs( int num, int sit, int use ) {
// 填满了,计算宽度
if( use==0 ) {
judge( num-1 ); return;
}
//如果父节点不是木棍,它不能填,填下一个
if( t[num/2]!=-1 ) {
dfs( num+1,sit,use );
}
else {
//当前可以填写的位置不够 则尝试把当前节点作为悬挂点扩充
if( sit<use ) {
t[num] = -1;
dfs( num+1,sit+1,use );
t[num] = 0;
}
//如果当前可供填写的节点只有一个 且需要填写的节点多于1个 那么不扩充num的话 后面就没法填了 回退
if( sit==1&&use>1 ) return;
//尝试在num上填写各种东西
for( int i=1; i<=s; ++i ) {
if( !vis[i] ) {
t[num] = i;
vis[i] = true;
dfs( num+1, sit-1, use-1 );
t[num] = 0;
vis[i] = false;
}
}
}
}
int main()
{
scanf("%d", &T);
while( T-- ) {
memset( w,0,sizeof(w) );
memset( t,0,sizeof(t) );
memset( vis,false,sizeof(vis) );
scanf("%lf %d", &wide, &s);
for( int i=1; i<=s; ++i ) scanf("%lf", &w[i]);
t[1] = -1;
ans = -1;
if( s==1 ) {
printf("%.16lf\n",0.0);
}
else {
dfs( 2,2,s );
if(ans==-1) printf("-1\n");
else printf("%.16lf\n",ans);
}
}
return 0;
}