刷题向》DP》值得一做》关于对DP问题的充分考虑(normal)
在你辛苦调试一道DP题,遇到瓶颈的时候,你是否感到一股洪荒之力遏制住你的思想,使你给题库贡献了一倍的WA、RE、TLE量,却没有AC过一次?
在这时,你应该考虑的是砸电脑再次重新考虑整个题目,再应对自己的思路考虑,是否你的思路对应这个题目有局限性?
那在这里,就给大家提供一道基础的扩展思路完全性的题目:分蛋糕
题目如下:
有一块矩形大蛋糕,长和宽分别是整数w 、h。现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形、且长和宽均为整数。切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕。请计算:最后得到的m块小蛋糕中,最大的那块蛋糕的面积下限。
假设w= 4, h= 4, m= 4,则下面的切法可使得其中最大蛋糕块的面积最小。
那么关于这道题,我的标程是在网上找的(U盘丢了→_→),虽然这道题只是一道平常的DP题,但是它让我学到了在解决DP问题的时候要考虑解决问题的完全性。
首先看标程,在标程里,在程序里,每当我们确定要分的块数、以及当前被分的蛋糕的长、宽的时候,我们第一步是先枚举这一刀要切在哪里,先得出一个当前能求出来的最小值,先把它记录在当前数组里,然后就没了。
怎么可能没了!!!当我们求出这一刀切在哪里的时候,才是我们这个程序最精彩的地方,想通这点,我们便可以直接get到以后DP的一个思路,我管它叫分锅(滑稽脸)。
那么我们是怎么进行分锅的捏,很简单,当我们确定这一刀分在哪里的时候,思路会有一个局限性:已经枚举所有情况。但是我们会发现在对于当前你的刀分的地方,是严重不平衡的,一半是充满的由以前的数据得出的最优分刀情况,一半是一刀也没有切,事实上,对于DP,当前所枚举的东西是当前确定的,而关于与当前步骤无关的一切都是由前面所递推取最优所得到的部分解,所以,在我们只确定当前刀的情况下,关于当前空间的其他状况都要另作考虑(也就是考虑更小部分的最优解),所以才有了标程里的又一重循环:在确认当前步骤的情况下(分锅)寻找剩余部分可能的最优解。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 #define N 25 5 #define INF 5005 6 int f[N][N][N];int w,h,m; 7 int main(){ 8 w=h=m=20; 9 for(int i=1;i<=w;i++){ 10 for(int j=1;j<=h;j++){ 11 f[i][j][1]=i*j; 12 for(int k=2;k<=m;k++){ 13 f[i][j][k]=INF; 14 for(int r=1;r<i;r++){ 15 f[i][j][k]=min(f[i][j][k],max(f[r][j][k-1],(i-r)*j)); 16 for(int p=1;p<k;p++) 17 f[i][j][k]=min(f[i][j][k],max(f[r][j][p],f[i-r][j][k-p])); 18 } 19 20 for(int c=1;c<j;c++){ 21 f[i][j][k]=min(f[i][j][k],max(f[i][c][k-1],(j-c)*i)); 22 for(int p=1;p<k;p++) 23 f[i][j][k]=min(f[i][j][k],max(f[i][c][p],f[i][j-c][k-p])); 24 } 25 } 26 } 27 } 28 while(scanf("%d%d%d",&w,&h,&m)&&(w||h||m)){ 29 printf("%d\n",f[w][h][m]);} 30 return 0; 31 }