1.6.8天平难题

举报
irrational 发表于 2022/01/17 22:51:57 2022/01/17
【摘要】 Description 给出房间的宽度r和s个挂坠的重量wi​,设计一个尽量宽(但宽度不超过房间宽度r)的天平,挂着所有的挂坠。天平由一些长度为1的木棍组成。木棍的每一端要么挂着一个挂坠,要么挂着另一个木棍。如图所示,设n和m分别是两端挂的总重量,要让天平平衡,必须满足na=mb(力矩相等) 例如,如果有3...

Description

给出房间的宽度r和s个挂坠的重量wi​,设计一个尽量宽(但宽度不超过房间宽度r)的天平,挂着所有的挂坠。天平由一些长度为1的木棍组成。木棍的每一端要么挂着一个挂坠,要么挂着另一个木棍。如图所示,设n和m分别是两端挂的总重量,要让天平平衡,必须满足na=mb(力矩相等)

1.png

例如,如果有3个重量分别为1,1,2的挂坠,有3种平衡的天平,如图所示。

2.png

挂坠的宽度忽略不计,且不同的子天平可以互相重叠,如图所示,宽度为(1/3)+1+(1/4)。

3.png

Input

输入第一行为数据组数。每组数据前两行为房间宽度r和挂坠的数量s(0<r<10,1<=s<=6)。以下s行每行为一个挂坠的重量Wi​(1<=Wi​<=1000)。输入保证不存在天平的宽度恰好在r−10^5和r+10^5之间(这样可以保证不会出现精度问题)。

Output

对于每组数据,输出最优天平的宽度。如果无解,输出−1。

你的输出和标准答案的绝对误差不应该超过1e−8

Sample Input 1

5
1.3
3
1
2
1
1.4
3
1
2
1
2.0
3
1
2
1
1.59
4
2
1
1
3
1.7143
4
1
2
3
5

Sample Output 1

-1
1.3333333333333335
1.6666666666666667
1.5833333333333335
1.7142857142857142

一个天平可看做一棵二叉树,对于一个确定的二叉树,可以算出每一个挂坠的位置,那么这个天平的宽度也可以计算出来,那么问题就转化成了枚举所有的二叉树。

在这里给出三种方法。

方法一:

自底上向上构造,每次任选择2个挂坠合并为一个。


  
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<iomanip>
  5. #define f(i,l,r) for(i=(l);i<=(r);i++)
  6. using namespace std;
  7. const int MAXN=8;
  8. double W,w[MAXN],ans,l[MAXN],r[MAXN];
  9. int n,vis[MAXN];
  10. inline void dfs(int cur)
  11. {
  12. int i,j;
  13. if(cur==n){
  14. f(i,1,n){
  15. if(vis[i]) continue;
  16. if(l[i]+r[i]>W) continue;
  17. ans=max(ans,l[i]+r[i]);
  18. }
  19. return;
  20. }
  21. f(i,1,n){
  22. if(vis[i]) continue;
  23. f(j,1,n){
  24. if(i==j||vis[j]) continue;
  25. vis[i]=1;
  26. double a=w[j]/(w[i]+w[j]),b=1-a;
  27. w[j]+=w[i];
  28. double tmpl=l[j],tmpr=r[j];
  29. l[j]=max(l[i]+a,-b+l[j]);
  30. r[j]=max(r[j]+b,-a+r[i]);
  31. dfs(cur+1);
  32. vis[i]=0;
  33. w[j]-=w[i];
  34. l[j]=tmpl;
  35. r[j]=tmpr;
  36. }
  37. }
  38. }
  39. int main()
  40. {
  41. ios::sync_with_stdio(false);
  42. int i,j,T;
  43. cin>>T;
  44. while(T--){
  45. memset(vis,0,sizeof(vis));
  46. memset(l,0,sizeof(l));
  47. memset(r,0,sizeof(r));
  48. ans=-1;
  49. cin>>W>>n;
  50. f(i,1,n){
  51. cin>>w[i];
  52. }
  53. dfs(1);
  54. cout<<fixed<<setprecision(10)<<ans<<endl;
  55. }
  56. return 0;
  57. }

方法二:自顶向下回溯构造,用一个一维数组保存二叉树,i的父亲就是i+i。


  
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<iomanip>
  5. #define f(i,l,r) for(i=(l);i<=(r);i++)
  6. #define ff(i,r,l) for(i=(r);i>=(l);i--)
  7. using namespace std;
  8. const int MAXN=8;
  9. double w[MAXN],W,l[1<<MAXN],r[1<<MAXN],val[1<<MAXN],ans;
  10. int n,tree[1<<MAXN],vis[MAXN];
  11. inline void judge(int cur)
  12. {
  13. int i;
  14. memset(l,0,sizeof(l));
  15. memset(r,0,sizeof(r));
  16. memset(val,0,sizeof(val));
  17. ff(i,cur,1){
  18. if(~tree[i]){
  19. val[i]=w[tree[i]];
  20. }
  21. else{
  22. int lson=i<<1,rson=lson|1;
  23. val[i]=val[lson]+val[rson];
  24. double a=val[rson]/val[i],b=1-a;
  25. l[i]=max(l[lson]+a,-b+l[rson]);
  26. r[i]=max(r[rson]+b,-a+r[lson]);
  27. if(l[i]+r[i]>W) return;
  28. }
  29. }
  30. ans=max(ans,l[1]+r[1]);
  31. }
  32. inline void dfs(int cur,int pos,int res)
  33. {
  34. int i;
  35. if(res==0){
  36. judge(cur-1);
  37. return;
  38. }
  39. if(~tree[cur>>1]){
  40. dfs(cur+1,pos,res);
  41. return;
  42. }
  43. if(pos<res){
  44. tree[cur]=-1;
  45. dfs(cur+1,pos+1,res);
  46. tree[cur]=0;
  47. }
  48. if(pos==1&&res>pos) return;
  49. f(i,1,n){
  50. if(vis[i]) continue;
  51. vis[i]=1;
  52. tree[cur]=i;
  53. dfs(cur+1,pos-1,res-1);
  54. vis[i]=0;
  55. }
  56. }
  57. int main()
  58. {
  59. ios::sync_with_stdio(false);
  60. int i,j,T;
  61. cin>>T;
  62. while(T--){
  63. memset(vis,0,sizeof(vis));
  64. memset(tree,0,sizeof(tree));
  65. ans=-1;
  66. cin>>W>>n;
  67. f(i,1,n){
  68. cin>>w[i];
  69. }
  70. if(n==1){ //necessary special judge
  71. cout<<0<<endl;
  72. continue;
  73. }
  74. tree[1]=-1;
  75. dfs(2,2,n);
  76. cout<<fixed<<setprecision(10)<<ans<<endl;
  77. }
  78. return 0;
  79. }

方法三:自顶向下枚举子集构造,每次枚举左子树用到哪些子集,那么右子树就是剩下的子集,递归构造即可,另外根据对称性,一个子集若已经构造过,那么剪枝。


  
  1. #include<iostream>
  2. #include<cstring>
  3. #include<vector>
  4. #include<algorithm>
  5. #include<iomanip>
  6. #define f(i,l,r) for(i=(l);i<=(r);i++)
  7. using namespace std;
  8. const int MAXN=10;
  9. struct Tree{
  10. double l,r;
  11. };
  12. vector<Tree> tree[MAXN<<3];
  13. double w[MAXN],W,sum[MAXN<<3],ans;
  14. int n,vis[MAXN<<3];
  15. inline void dfs(int S)
  16. {
  17. int i,j,l,r,flag=1;
  18. if(vis[S]) return;
  19. vis[S]=1;
  20. for(l=(S-1)&S;l;l=(l-1)&S){
  21. flag=0;
  22. r=S^l;
  23. double d1=sum[r]/sum[S];
  24. double d2=sum[l]/sum[S];
  25. dfs(l);
  26. dfs(r);
  27. for(i=0;i<tree[l].size();i++){
  28. for(j=0;j<tree[r].size();j++){
  29. Tree tmp;
  30. tmp.l=max(d1+tree[l][i].l,-d2+tree[r][j].l);
  31. tmp.r=max(d2+tree[r][j].r,-d1+tree[l][i].r);
  32. if(tmp.l+tmp.r<W) tree[S].push_back(tmp);
  33. }
  34. }
  35. }
  36. if(flag) tree[S].push_back((Tree){0,0});
  37. }
  38. int main()
  39. {
  40. ios::sync_with_stdio(false);
  41. int T,i,j,root;
  42. cin>>T;
  43. while(T--){
  44. cin>>W>>n;
  45. memset(sum,0,sizeof(sum));
  46. memset(vis,0,sizeof(vis));
  47. f(i,1,n){
  48. cin>>w[i];
  49. }
  50. f(i,1,(1<<n)-1){
  51. tree[i].clear();
  52. f(j,0,n-1){
  53. if(i&(1<<j)){
  54. sum[i]+=w[j+1];
  55. }
  56. }
  57. }
  58. root=(1<<n)-1;
  59. dfs(root);
  60. ans=-1;
  61. for(i=0;i<tree[root].size();i++){
  62. ans=max(ans,tree[root][i].l+tree[root][i].r);
  63. }
  64. cout<<fixed<<setprecision(10)<<ans<<endl;
  65. }
  66. return 0;
  67. }

之后会有补充,敬请期待!

文章来源: blog.csdn.net,作者:irrationality,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_54227557/article/details/120616271

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。