《算法设计编程实验:大学程序设计课程与竞赛训练教材》——2.3 构造法模拟的实验范例

简介: 本节书摘来自华章计算机《算法设计编程实验:大学程序设计课程与竞赛训练教材》一书中的第2章,第2.3节,作者:吴永辉,王建德著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.3 构造法模拟的实验范例

构造法模拟需要完整、精确地构造出反映问题本质的数学模型,根据该模型设计状态变化的参数,计算模拟结果。由于数学模型建立了客观事物间准确的运算关系,因此其效率一般比较高。
构造法模拟的关键是找到数学模型。问题是,能产生正确结果的数学模型并不是唯一的,从不同的思维角度看问题,可以得出不同的数学模型,而模拟效率和编程复杂度往往因数学模型而异。即便有数学模型,解该模型的准确方法是否有现成算法、编程复杂度如何,这些问题也需要我们仔细考虑。
【2.3.1 Packets】
【问题描述】
一家工厂生产的产品被包装在一个正方形的包装盒中,产品具有相同的高度h,大小规格为11、22、33、44、55和66。这些产品是用和产品具有相同高度h,大小规格为6*6的正方形邮包交付给客户的。由于费用问题,工厂和客户都要使将订购的物品从工厂发送给客户的邮包的数量最小化。请编写一个程序,对于按照订单发送给定的产品,找出最少的邮包数量,以节省费用。
输入:
输入由若干行组成,每行描述一份订单,每份订单由6个整数组成,整数之间用一个空格分开,连续的整数表示从最小的11到最大的66每种大小的包装盒的数量,输入以包含6个0的一行结束。
输出:
对每行输入,输出一行,给出邮包的最小数量。对于输入的最后一行“空输入”没有输出。
image

试题来源:ACM Central Europe 1996
在线测试地址:POJ 1017,ZOJ 1307,UVA 311
试题解析
这是一道构造法模拟题,其使用的数学模型是一个贪心策略——按照尺寸递减的顺序装入包装盒。由于邮包的尺寸为66,因此每个44、55和 66 的包装盒需要一个单独的邮包。
6*6:刚好。
55:放入66 的邮包中,剩下的用1*1填充。
44:放入66的邮包后,先用22填充, 没有22的就用1*1填充。
33:一个66的邮包可以放4个。
22和11一样操作。
具体实现方法如下。设i*i的包装盒数为ai(1≤i≤6):
放入66、55、44、33的包装盒至少需要邮包数M=a6+a5+a4+a34。
M个邮包可填入22的包装盒数L2=a45+u[a3 mod 4]u[0]=0,u[1]=5,u[2]=3,u[3]=1。若有剩余(a2>L2),则放入新增的a2-L29个邮包,即M+=a2-L29。
最后使用11的L1(=m36-a636-a525-a416-a39-a2*4)个包装盒填满M个邮包。若有剩余(a1>L1),则放入新增的a1-L136个邮包,即M+=a1-L136。
显然,M是放入所有包装盒的最少邮包数。
程序清单

#include <iostream>
using namespace std;

int main()
{
  int a[10],i,j,sum,m,left1,left2;// 每种尺寸的包装盒数为a[];包装盒总数为sum;使用的邮包数为m;当前
// 邮包可装入2*2的包装盒数为left2,可装入1*1的包装盒数为left1
  int u[4]={0,5,3,1};//  u[a[3]% 4]
  while (1)
  {
    sum=0;
    for(i=1;i<=6;i++)// 输入每种尺寸的包装盒数量,累计包装盒的总数
    {
       cin>>a[i];
       sum+=a[i];
    }
    if(sum==0) break;// 若输入6个0,则退出程序

    m=a[6]+a[5]+a[4]+(3+a[3])/4;// 计算放入前4种大尺寸的包装盒至少需要的邮包数
    left2=a[4]*5+u[a[3]%4];// 计算M个邮包可填入2*2的包装盒数
    if(a[2]>left2)// 若2*2的包装盒有剩余,则累计新增的邮包
       m+=(a[2]-left2+8)/9;
    left1=m*36-a[6]*36-a[5]*25-a[4]*16-a[3]*9-a[2]*4; 
// 填满上述邮包需要使用1*1的
// 包装盒数
    if(a[1]>left1)// 若1*1的包装盒有剩余,则累计新增的邮包
       m+=(a[1]-left1+35)/36;
    cout<<m<<endl;// 输出最少邮包数
  }
  return 0;
}

【2.3.2 Paper Cutting】
【问题描述】
ACM经理需要用名片将他们自己介绍给客户和合作伙伴。名片上的信息印刷到一大张纸上之后,要用一台特殊的切割机来切割纸张。由于机器的操作花费非常昂贵,因此要尽量减少切割的次数。请编写程序,找到切割产生名片的最佳解决方案。
切割有若干条必须遵守的限制。要以恰好a*b张名片的网格结构印刷名片。由于印刷软件的限制,结构的尺寸(在一行和列中名片的数量)是固定的,不能改变。纸张是矩形的,它的大小是固定的。网格必须垂直于纸张的边,也就是说,它只可以进行90°的旋转。但是,可以交换行和列的含义,卡片可以放置在纸张上的任何位置,卡片的边和纸张边可以重合。
例如,设定名片的大小是34厘米,网格大小为12张名片。图2.3-1给出网格的四个可能的方案。要求给出每种情况所需要的最小的纸张尺寸。
image

用于切割卡片的切割机能够进行任意长度的连续切割,切割能够贯穿整片的纸张,不会中途停止。一次切割只能针对一片纸张 —— 不能为了节省切割次数将纸张叠加起来切,也不能把纸张折叠起来切。
输入:
输入由若干测试用例组成,每个测试用例由在一行上的6个正整数A、B、C、D、E、F组成,整数间用一个空格分开,这些数的含义如下:
A和B是矩形网格的大小,1 ≤ A,B≤ 1000;C和D是卡片的尺寸,以厘米为单位,1 ≤ C,D ≤ 1000;E和F是纸张的尺寸,以厘米为单位,1 ≤E,F≤ 1000000。
输入以6个0的一行结束。
输出:
对每个测试用例,输出一行,该行给出“The minimum number of cuts is X.”,其中X是要求切割的最小次数。如果纸张不能符合卡片网格,则输出“The paper is too small.”来代替。
image

试题来源:CTU Open 2003
在线测试地址:POJ 1791,ZOJ 2160
试题解析
先在纸张中切网格,然后在网格内切名片。设矩形网格大小为ab、名片尺寸为cd、纸张尺寸为ef,即纵向有a张尺寸为c的名片,纵向总长为ac;横向有b张尺寸为d的名片,横向总长为bd。而名片的切割在纸张范围内进行,由此得出约束条件:(ac≤e)&&(b*d≤f)。
切出大小为ab的网格,至少需要ab-1次切割。若acdb-1+(ac1)网格大小为ba(转90°)、名片尺寸为cd(不变)、纸张尺寸为e*f(不变)。
2)网格大小为ab(不变)、名片尺寸为dc(转90°)、纸张尺寸为e*f(不变)。
3)网格大小为ba(转90°)、名片尺寸为dc(转90°)、纸张尺寸为e*f(不变)。
按照上述方法依次得出三种情况下的切割次数c1、c2、c3,如果其中任意一种情况不满足约束条件,则切割次数为∞。显然,最少切割次数为Ans=min{c0,c1,c2,c3}若Ans=∞,则说明纸张不能符合卡片网格,因为所有可能情况都不满足约束条件。
程序清单

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define TOOBIG INT_MAX
int ncuts(int a,int b,int c,int d,int e,int f) ;
void do_solve(int a,int b,int c,int d,int e,int f) // 输入矩形网格大小、卡片尺寸和纸张的尺寸,枚举
// 旋转的4种情况,计算最小切割次数
{
 int x,m ;
 m=ncuts(a,b,c,d,e,f) ; // 计算未旋转的切割次数
 if ((x=ncuts(b,a,c,d,e,f))<m) m=x ;// 网格转90°,调整最小切割次数
 if ((x=ncuts(a,b,d,c,e,f))<m) m=x ;// 卡片转90°,调整最小切割次数
 if ((x=ncuts(b,a,d,c,e,f))<m) m=x;// 网格和卡片各转90°,调整最小切割次数
 if (m==TOOBIG)
  puts("The paper is too small.");
 else
  printf("The minimum number of cuts is " "%d.\n",m) ;
}

int ncuts(int a,int b,int c,int d,int e,int f)// 计算当前情况下(矩形网格大小为(a,b)、卡片尺寸
// 为(c,d)、纸张尺寸为(e,f))的最小切割次数
{
 if (a*c>e || b*d>f) return TOOBIG ;// 若超出纸张范围,则返回∞
 return a*b-1+(a*c<e)+(b*d<f);// 返回切割次数
}
 
int main()
{ int a,b,c,d,e,f ;
 for(;;) {
  a=0 ; b=0 ; c=0 ; d=0 ; e=0 ; f=0 ;
  scanf("%d %d %d %d %d %d",&a,&b,&c,&d,&e,&f) ;// 输入矩形网格大小、卡片尺寸和纸张的尺寸
  if (!a && !b && !c && !d && !e && !f) break ;// 若全为0,则结束程序
  do_solve(a,b,c,d,e,f) ;
 }
 return 0 ;
}
相关文章
|
6月前
|
算法
2017级《算法设计与分析》--实验1--分治算法-骨牌铺方格
2017级《算法设计与分析》--实验1--分治算法-骨牌铺方格
|
6月前
|
算法
2017级《算法设计与分析》--实验1--分治算法
2017级《算法设计与分析》--实验1--分治算法
|
6月前
|
机器学习/深度学习 数据采集 算法
【机器学习】基于机器学习的分类算法对比实验
【机器学习】基于机器学习的分类算法对比实验
131 6
【机器学习】基于机器学习的分类算法对比实验
|
1月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
107 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
1月前
|
算法
计科一二班算法数据结构实验9答案
计科一二班算法数据结构实验9答案
14 0
|
3月前
|
算法 Java 测试技术
算法分析(蛮力法与减治算法应用实验报告)
这篇文章是关于算法分析的实验报告,介绍了如何使用蛮力法解决背包问题,并通过伪代码和Java代码实现,同时分析了其时间效率;还介绍了基于减治法思想实现的二叉查找树的插入与查找,同样提供了伪代码、Java源代码实现和时间效率分析,最后展示了测试结果截图。
算法分析(蛮力法与减治算法应用实验报告)
|
3月前
|
算法 C++
第一周算法设计与分析 E : 构造回文串
这篇文章介绍了解决算法问题"构造回文串"的方法,即判断给定的整数N(视为字符串)是否可以通过在前面添加任意个0(或不添加)来构造一个回文串,并给出了相应的C++代码实现。
|
3月前
|
机器学习/深度学习 算法 Java
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
这篇文章介绍了基于贪婪技术思想的Prim算法和Dijkstra算法,包括它们的伪代码描述、Java源代码实现、时间效率分析,并展示了算法的测试用例结果,使读者对贪婪技术及其应用有了更深入的理解。
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
|
3月前
|
算法 Java 测试技术
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
这篇文章介绍了基于动态规划法的三种算法:解决背包问题的递归和自底向上实现、Warshall算法和Floyd算法,并提供了它们的伪代码、Java源代码实现以及时间效率分析。
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
|
3月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
下一篇
无影云桌面