UVa-1354天平难题

参考: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;
}



猜你喜欢

转载自blog.csdn.net/CY05627/article/details/87278840
今日推荐