UVA 1354 Mobile Computing 回溯

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83719191

原题链接

给出房间宽度r(10.0)和挂坠数s(6),以及s个挂坠的重量(1000)。现在有若干长度为1的木棒,木棒的每一端要么挂着一个木棒,要么挂着一个挂坠。用它们来设计一个尽量宽的天平(宽度不能超过房间宽度),求它的宽度(无解输出-1).

可以把整个天平看成一棵叶子是挂坠的满二叉树,当树确定后就可以唯一的确定宽度。
使用回溯法来枚举二叉树:

方法一:每次选择两个节点组成一棵子树,然后把新子树看成一个节点进行递归。
每个节点实际上只有三个属性:重量,重心左边的宽度,重心右边的宽度。
初始的挂坠只有重量,两边的宽度都是0.
逐层合并,更新答案。

struct Item{
	int wei;
	double lef, rig;
};

Item merge(Item A, Item B)
{
	int wei = A.wei + B.wei;
	double l0 = (double)B.wei/wei, r0 = (double)A.wei/wei;
	double lef = max(l0+A.lef,B.lef-r0);
	double rig = max(r0+B.rig,A.rig-l0);
	return Item({wei,lef,rig});
}
double r,ans;
int n;
void search(vector<Item> vc)
{
	if(vc.size()==1)
	{
		double tmp = vc[0].lef + vc[0].rig;
		if(tmp<=r) ans = max(ans,tmp);
		return;
	}
	int lim = vc.size();
	//什么破枝老子不剪了
	//不行, 复杂度重要
	for(int i=0;i<lim;++i)
		for(int j=i+1;j<lim;++j)
		{
			vector<Item> nc = vc;
			nc.erase(nc.begin()+j), nc.erase(nc.begin()+i);
			nc.push_back(merge(vc[i],vc[j]));
			search(nc);
			nc.back() = merge(vc[j],vc[i]);
			search(nc);
		}
}

void solve()
{
	ans = -1;
	scanf("%lf %d",&r,&n);
	vector<Item> vc; 
	for(int i=0;i<n;++i)	
		vc.push_back(Item({read(),0,0}));
	search(vc);
}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int t = read();
	while(t--)
	{
		solve();
		if(ans<0)
			printf("-1\n");
		else
			printf("%.15f\n",ans );
	}
    return 0;
}

最开始写的很麻烦,为每种节点建立了索引和排序规则,还写了很多剪枝。后来发现没有必要,直接比较就好。

提前优化是万恶之源。

方法2:还不会,学完第九章再补。

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/83719191