边长为n的正方形最多可以放下多少个半径为r的圆?_怎么计算矩形里面可以放入最多数量的圆-程序员宅基地

技术标签: 算法  趣味题  ACM算法  

今天看见数院群里有人在讨论一道有意思的题目,题意好像是这样的:在一个10x10的正方形里最多可以放多少个半径为1圆?在知乎里找到了10*10的正方形能放多少个直径为1的圆,那么最优的放置方法如下:
在这里插入图片描述
从图中可以看出,并不是每一排放10个,放10排是最优的。因为这样会造成中间的空隙很大。可以看出可能更优的放置方法是:交错着放,即(图中从下往上看):第一排放10个,第二排放9个,第三排放10个。第二排的每一个都放在两个的中间,虽然这样第二排少放了一个,但是它给占用的高度更小了。如果一直这样放就有可能使得累计出来的高度多放一排。但是我们却不知道用减少个数来换取余留下来的高度是否是值得的。(比如下图所示:正方形边长是3,那么交错放,只能放3+2+3=8个,这样红线以下余留下来的高度不足以再放下一排)
在这里插入图片描述
为了简化问题,只讨论正方形边长能被圆直径整除的情况。
应该怎样决策呢?现在已经确定最优的放法就是每一排要么放满n个,要么和上一排交错放,放n-1个或者n个,因为这样能最少的占用高度。
如果不交错放,那么它占用的高度就是2r。如果交错放,用勾股定理算出占用的高度是√3r。如下图所示:
在这里插入图片描述这样很容易想到用动态规划来解决这个问题。因为它的决策阶段很明显被划分成了一排一排的,而每一排有2种决策方法。由于还要记录它决策过程中的高度。所以要用一维来方便计算出目前放置的高度。可以增加一个状态来表示交错放置了多少次。这样来定义状态:用f[i][j][k]来表示:前i行中,交错放置了j次,并且第i行的放置状态为k时,最多放置圆的个数。其中k只有两种取值,k=0时表示这一排放满s个,k=1时表示这一排放s-1个(其中s=n/2r)。判断是否是加错放只需要判断上一行的k和这一行的k是否不相等。同时呢,还需要计算它当前的高度,可以用g[i][j][k]来记录高度。

这样它们的这状态转移方程很容易写出来:
记每一排放满恰好能放s个。

f[i][j][0]=max(f[i-1][j-1][1],f[i-1][j][0])+s
g[i][j][0]=g[i-1][j-1][1]+√3r , 当f[i-1][j-1][0]<f[i-1][j][1]时
g[i][j][0]=g[i-1][j][0]+2r , 当f[i-1][j-1][0]>f[i-1][j][1]时

解释:f[i][j][0]:前i排中交错放了j次,并且第i排放满时,最多放的个数。它可以由上一排也放满了时转移过来,因为这排和上一排都放了s个,所以加错次数不变,上一排k的状态是0,即:f[i-1][j][0]。也可以由上一行没有放满的状态,这时交错次数增加了1,上一排k的状态是1,所以由f[i-1][j-1][1]转移过来。这两者取一个最大值后加上这一排放置的个数就作为f[i][j][0]的值。高度g的计算是根据前后的k是否一样转移的(k相等,增加2r,否则增加√3r)。

同理有:
f[i][j][1]=f[i-1][j-1][0]+s-1
g[i][j][1]=g[i-1][j-1][0]+√3*r

解释:这里的转移状态只有一个,原因是当前第i排放的是s-1个,如果上一排还是放的s-1个,肯定不是最优的,因为这样不仅占用2r高,这排还少放一个,怎么也划不来。所以不用从f[i-1][j][1]转移过来。

最终答案就是所有g[i][j][k]<=n的状态中的max(f[i][j][k])

有了答案放置方法就可以倒推回去了,如10*10的正方形,直径是1的圆,最终答案是106。这个答案在f[11][8][0]里面,那么可以得出共放了11排,其中交错放置了8次。复原放法很简单,前9排交错放(共8次交错)。
即:第一排放10个,第二排放9个,第三排放10个,第四排放9个…,第九排放10个,之后的10-11排都放10个。

用c++实现如下:

#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;
const int N=500;
int f[N][N][2];
double g[N][N][2];
int main()
{
    
    while(true)
    {
    
        int n,d;
        cout<<"请输入正方形的边长:";
        cin>>n;
        cout<<"请输入圆的直径: ";
        cin>>d;
        cout<<endl;
        int nn=n;
        int D=d;
        if(d%2!=0)
            n*=2,d*=2;
        double r=d/2;
        int s=n/d;
        int ans=0;
        double sqrt3=sqrt(3);

        //cout<<n<<" "<<d<<" "<<sqrt3<<" "<<s<<endl;
        int ii,jj;
        for(int i=1; i<=n; i++)
        {
    
            for(int j=0; j<=i; j++)
            {
    

                if(j>0&&f[i-1][j-1][1]>f[i-1][j][0])
                {
    
                    f[i][j][0]=f[i-1][j-1][1]+s;
                    g[i][j][0]=g[i-1][j-1][1]+sqrt3*r;
                }
                else
                {
    
                    f[i][j][0]=f[i-1][j][0]+s;
                    g[i][j][0]=g[i-1][j][0]+2*r;
                }
                if(j>0)
                {
    

                    f[i][j][1]=f[i-1][j-1][0]+s-1;
                    g[i][j][1]=g[i-1][j-1][0]+sqrt3*r;
                }
                if(g[i][j][0]<=n)
                    if(ans<f[i][j][0])
                        ans=f[i][j][0],ii=i,jj=j;
                if(g[i][j][1]<=n)
                    if(ans<f[i][j][1])ans=f[i][j][1],ii=i,jj=j;
            }
        }

        printf("边长为%d的正方形圆的直径为%d,可以放下:  %d   个\n",nn,D,ans);
        printf("共放置 %d 排,其中交错放置 %d 次\n\n",ii,jj);
    }

}

经过测试,结果如下:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41832757/article/details/102961620

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法