版权声明:欢迎评论交流,转载请注明原作者。 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:还不会,学完第九章再补。