问题描述:
有一块矩形大蛋糕,宽和高分别是整数w、h。 现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形,且宽和高均为整数。切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕。请计算:最后得到的n块小蛋糕中,最大的那块蛋糕的面积下限。
例如:
假设w= 4, h= 4, m= 4,则下面的切法可使得其中最大蛋糕块的面积最小(m=4则切三刀,分别书1.竖刀,2.左横刀,3.右横刀);
假设w= 4, h= 4, m= 3,则下面的切法可使得其中最大蛋糕块的面积最小(m=3则切两刀,1.竖刀,2.横刀)。
输入
共有多行,每行表示一个测试案例。每行是三个用空格分开的整数W, H, M,其中1≤W,H,M≤20,M≤W*H。当W=H=M=0时不需要处理,表示输入结束。
输出
每个测试案例的结果占一行, 输出一个整数,表示最大蛋糕块的面积下限。
思路:
1.较复杂的动态规划问题,是求最值一类的问题;
2.因为不会有交叉刀,所以每一刀都是由1块变为两块,因此需要切m-1刀。而且,蛋糕块数从1依次变到m,不会出现倍数增加,大大简化了问题;
//ways(w,h,m)=min(row,column);第一刀为横或竖,取这两种的最小值
//row=min(si);//以横刀为例,如果是横刀,这一刀切在哪里影响着面积的大小,取最小值
//si=max(ways(w,i,j),ways(w,h-i,m-j))当这一刀位置确定,切在h为i处,那么把蛋糕分为了两部分
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int num[20][3];
int ways(int w,int h,int m)
{
if(w*h<m)return 400;//边界条件
if(m==1)return w*h;
int r[h],c[w];//分别代表row向和column向
for(int i=1;i<h;i++){
r[i]=400;
for(int j=1;j<m;j++){
int rowmin=max(ways(w,i,j),ways(w,h-i,m-j));
if(rowmin<r[i])r[i]=rowmin;
}
}
for(int i=1;i<w;i++){
c[i]=400;
for(int j=1;j<m;j++){
int columnmin=max(ways(i,h,j),ways(w-i,h,m-j));
if(columnmin<c[i])c[i]=columnmin;
}
}
int row=*min_element(r+1,r+h);
int column=*min_element(c+1,c+w);
return min(row,column);
}
int main()
{
int n=1;
for(int i=0;i<20;i++){
cin>>num[i][0]>>num[i][1]>>num[i][2];
if(num[i][0]==0)break;
n++;
}
for(int i=0;i<n;i++){
cout<<ways(num[i][0],num[i][1],num[i][2])<<endl;
}
}
3.以函数ways(w,h,m)表示w*h大的蛋糕切成m块最小的最大面积,边界条件有两个:首先,w*h要大于等于m,m块蛋糕面积最小为m,如果w*h还要小说明方法错误,返回20*20即可;其次,当m=1时,无法再切了,返回w*h即可;
4.首先看第一刀,有两种选择:横刀或者竖刀。这属于两类,需要取这两类的较小值;
5.以横刀为例,这一刀切在哪里影响着面积的大小,所以需要遍历1到h-1,找出其中的最小值;
6.当第一刀的位置确定,切在h=i处时,蛋糕被分为两部分,一部分为(w,i),另一部分为(w,h-i),这两部分一共会被分为m块,但是每一块被分成的块数也会影响面积大小,如果一个被分为j块,另一个则被分为m-j块,j的大小也会影响面积。所以要从1到m-1遍历j,遍历的时候比较ways(w,i,j)和ways(w,h-i,m-j),取最大值(原因详见“7”),然后取m-1个最大值中的最小值;
7.因为ways(w,i,j)和ways(w,h-i,m-j)是同时出现的,如果取前者,若后者出现更大的面积,那么也只能妥协取后者较大的面积,因为你要求的就是每一种情况下的最大面积,这种情况下的最大面积才是结果。
8。双循环,外层循环i代表第一刀的位置,当i确定后,内层循环循环两侧的块数j;先从两个ways中取最大值,内层共有m-1个最大值,在其中选择最小值,这个最小值一共有h-1个,即外层循环,再从这最小值中选择最小值,作为row,同样的方法求出column,比较取较小值即可。