(UVA 1354) Mobile Computing(子集枚举+深搜剪枝)

原题连接:UVA 1354

题目大意
在这里插入图片描述
在这里插入图片描述
题目分析
可以把挂坠和横放的木棍都看成结点,则整个天平就是一个二叉树,且每个结点要么是叶子结点要么是有2个孩子的内部结点,例如上图中的3种天平就对应于下图3个二叉树:
在这里插入图片描述
而且不同的天平之间可以重叠。同时,对于一棵确定的二叉树,可以计算出每个天平的确切位置,进而计算出整个天平的宽度。所以,本题的核心是:如何枚举出所有需要的二叉树。下面介绍两种方法:

自底向上枚举

因为二叉树有s个叶子结点,每个内部结点2个孩子,所以一共有s-1个内部结点,即总共2s-1个结点。
所以我们可以:

  • 将初始的s个挂坠看成s个子树
  • 每次选择两个挂坠形成新的子树加入,然后递归s-1次,则一共形成了2s-1个结点
  • 注意每次形成新子树的时候,都需要判断一下宽度是否溢出,若是则剪枝
  • 例如以4个挂坠{1,1,2,3}为例,下面画出解答树的一部分:(每个结点所能形成的所有子树并未完全画出)
  • 在这里插入图片描述
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;
// 自下而上的递归
const int maxs = 6; // 最多的挂坠数
const double EPS = 1E-9; // 最大误差精度
struct node {
   
	double w; // 重量
	double left, right; // 左右子树的最大宽度
	node() :left(0.0), right(0.0), w(0) {
   }
}Node[2 * maxs]; // s个叶子结点和s-1个内部结点,一共2s-1个结点
int vis[2 * maxs];
double r;
int s; // 宽度和挂坠数目
double maxr; // 求得的最大宽度

int isRight(node n) {
   
	// 检验宽度是否合格
	double width = n.left + n.right;
	if (width <= r + EPS) return 1;
	else return 0;
}
void init() {
   
	maxr = -1;
	memset(vis, 0, sizeof(vis));
	cin >> r >> s;
	for (int i = 0; i < s; i++) {
   
		cin >> Node[i].w;
		Node[i].left = Node[i].right = 0;
	}
}

void dfs(int index) {
   
	// 构造第index个结点 0-2s-2
	if (index == 2 * s - 1) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值