石材切割问题
题目描述
题目分析
输入输出
解决方案
题目描述
题目分析
这是一个课堂思考题,部分描述不明确。题目大概是这几个条件:
①石板长度为L和W,切割出来的石砖个数没有要求,但需要利用率最高。题目未给出石砖共有几种类型,这里假设共有n种。
②长度方向保持一致即石砖在切割之前长和宽分别与石板平行。
③一刀切即从一端切到另一端。
这是一个二分操作,同时要求记录切割的方法,我们很容易想到的数据结构就是二叉树。每一个结点包含的信息应包括石板尺寸、该石板最大有效面积、最佳切法和子石板的相关信息(作为左右子树的根结点)。
对于每一块石板分三种情况:
①正好是某类石砖的尺寸,则该节点的部分信息应为石板尺寸、石板面积。
②石板的长和宽均大于或等于某个石砖的长和宽,则遍历所有尺寸的石砖。对于每一个尺寸的石砖,若可切割,则分两种切割方法,有效面积等于两个子石板的有效面积之和,然后取有效面积较大的方法。最后取所有情况中有效面积最大的一种作为该节点的最大有效面积,并赋值予该石板的切割方法以及该方法所分出的两个子石板的相关信息。
③边角料,即不可利用,返回NULL。
考虑到开辟空间的问题,这里还写了一个abandon函数,用于释放无用链表的空间。
构建完二叉树后,对该树进行先序遍历,输出每一步的切割方法并释放该节点即可。在实际应用中,由于相同尺寸的石板的切割方法是一样的,所以递归到某个结点时可随便挑选一块相应尺寸的石板即可。
输入输出
由于这只是一个课堂思考题,所以题目并没有明确规定输入和输出的格式。这里按照我写的程序进行规定
输入:
第一行包含三个正数,分别是n,L和W。
接下来n行输入每一类石砖的尺寸length[i]和wide[i]。
输出:
每一行按照“将尺寸为l*w的石板在'w'上len位置切一刀”的格式输出。
输入样例1:
2 6 3
2 2
5 1
输出样例1:
将尺寸为6*3的石板在w上2位置切一刀
将尺寸为6*2的石板在l上2位置切一刀
将尺寸为4*2的石板在l上2位置切一刀
将尺寸为6*1的石板在l上5位置切一刀
最大有效面积为17
输入样例2:
2 3 6
2 2
5 1
输出样例2:
将尺寸为3*6的石板在w上2位置切一刀
将尺寸为3*2的石板在l上2位置切一刀
将尺寸为3*4的石板在w上2位置切一刀
将尺寸为3*2的石板在l上2位置切一刀
将尺寸为3*2的石板在l上2位置切一刀
最大有效面积为12
解决方案:
#include <iostream>
using namespace std;
typedef struct a{
int l,w,s; //切之前的长、宽,以及该石板最大有效面积
char CUT; //'w','l'或者'0',表示切的最有效切法的方向或者不需要再切
int len; //表示切的长度,为自然数
struct a *left,*right; //表示切完之后的两块子石板,作为左右子树
}tree;
int wide[100],length[100],n,L,W; //所需小石板的尺寸、小石板的类型数以及大石板的尺寸
void abandon(tree *head){ //释放边角料子石板的空间
if(head){
abandon(head->left);
abandon(head->right);
free(head);
head=NULL;
}
}
tree *cut(int l1,int w1){
//第一种情况,恰好得到所需尺寸
for(int i=0;i<n;i++)
if(wide[i]==w1&&length[i]==l1){
tree *p=new tree;
p->l=l1;
p->w=w1;
p->CUT='0';
p->s=l1*w1;
p->len=0;
p->left=p->right=NULL;
return p;
}
tree *p=new tree; //用作最大有效面积的比较筛选和赋值,初始为0
p->l=l1;
p->w=w1;
p->s=p->len=0;
p->CUT='0';
p->left=p->right=NULL;
bool isShort=true; //判断是否为边角料,即不可切且不符条件
//第二和第三种情况,可切或者是边角料,通过遍历每一种需求的尺寸进行判断
for(int i=0;i<n;i++){
if(wide[i]<=w1&&length[i]<=l1){
isShort=false; //可切,非边角料
tree *q1,*q2; //切完后的两个子石板
//纵向切取最有效切法
if(w1>wide[i]){
int s1=0,s2=0;
q1=cut(l1,wide[i]);
q2=cut(l1,w1-wide[i]);
if(q1) //空指针的有效面积为零
s1=q1->s;
if(q2)
s2=q2->s;
if(s1+s2>p->s){ //取大赋值
p->CUT='w';
p->s=s1+s2;
p->len=wide[i];
abandon(p->left);
abandon(p->right);
p->left=q1;
p->right=q2;
}
else{
abandon(q1);
abandon(q2);
}
}
//横向切取最有效切法,算法同上
if(l1>length[i]){
int s1=0,s2=0;
q1=cut(length[i],w1);
q2=cut(l1-length[i],w1);
if(q1)
s1=q1->s;
if(q2)
s2=q2->s;
if(s1+s2>p->s){
p->CUT='l';
p->s=s1+s2;
p->len=length[i];
abandon(p->left);
abandon(p->right);
p->left=q1;
p->right=q2;
}
else{
abandon(q1);
abandon(q2);
}
}
}
}
if(isShort){ //边角料,置空
free(p);
p=NULL;
}
return p;
}
void preorder(tree *head){ //先序遍历输出各个步骤的切法
if(head){
if(head->len)
cout<<"将尺寸为"<<head->l<<'*'<<head->w<<"的石板在"<<head->CUT<<"上"<<head->len<<"位置切一刀"<<endl;
preorder(head->left);
preorder(head->right);
}
}
int main(){
cin>>n>>L>>W;
for(int i=0;i<n;i++)
cin>>length[i]>>wide[i];
tree *head=cut(L,W);
preorder(head);
cout<<"最大有效面积为";
if(head)
cout<<head->s;
else
cout<<0;
abandon(head);
return 0;
}