USACO Section 1.4 packrec

来源:互联网 发布:淘宝网天猫男装外套 编辑:程序博客网 时间:2024/05/15 07:49

一开始无从下手。后来想着情况也不是很多,可以每种情况都分别枚举,这时位置固定之后,计算长宽就相对简单了。


1. 四个矩形摆放,有多少种最基本的方式? 题目给了6种基本型.我按照 矩形承载的矩形数目 来分类。

    (PS:这里我想到离数的 集合划分和等价关系。 只要证明该关系是传递,自反,对称的, 那么利用该关系就能 不重不漏的划分一个集合。

                  两种摆放是有关系的,如果上方的矩形个数是相同的。似乎三个性质都满足,所以似乎是一个等价关系。虽然关系的这个定义不那么明确。

                 这样把所有摆放分成了4类, 但是2个矩形的情况有两种无法合并,即下表的3,4。恩,确实是很不成熟的想法。

      )

         
1  并排,上方没其他矩形2 这两种可以合并,矩形上方仅有1个矩形3 矩形上方2个矩形4  矩形上方2个矩形5 矩形上方有三个    

       而且枚举的范围无需 A(4,4)! * 16, 四个矩形的全排列,2^4种长宽搭配。

        case 1:四个并排,矩形的顺序没有影响,所以只需枚举16种。

        case 2: 2个矩形的上下顺序没有影响,另外两个矩形的左右顺序也没有影响,C(2,4)*16 即可

        case 3:田字形关键在于对角线,确定一组对角线,另外两块的顺序没有影响。所以只有3种对角线, 3*16种。

        case 4:矩形上方的两块的顺序没有影响。另外两块的顺序有影响,所以 C(2,4)*2*16 = 192

        case 5:上方的三块顺序没有影响,所以4*16种。

2. 大方向已经定好了,就是写几个函数分别计算这五种情况的enclosing rectangle的长宽。接下来是处理16种长宽搭配,

长宽搭配的不同就是把给定位置上的矩形旋转90度,或者说交换长宽。可以使用循环,递归,或者枚举处理16种情况。

我用的是循环。0~15, 四位二进制表示中,1代表旋转,0代表不旋转。


3. 计算 enclosing rectangle时,case 3田字形特别麻烦,要判断重叠。

剩下的就是仔细考虑如何计算长宽, 还有debug了。

   

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>struct rectangle{int a, b;}; typedef struct rectangle Rectangle;Rectangle solution[450];  //enclosing rectangleint count=0;  // number of possible solutionint minL=51, minW=51, minArea=20000;int Max( int a, int b){return (a>b)? a:b;}Rectangle rotate_90( Rectangle r ){Rectangle t = { .a = r.b,  .b = r.a};return t;}void setConbination( Rectangle* des, const Rectangle* ori, int indictator){int j=0;for(; j<4; j++)  des[j] = (indictator&(1<<j))? rotate_90(ori[j]) : ori[j];  }void setSolution( int L, int W ){if( L*W > minArea ) return;minArea = L*W;int t;if( L > W ) { t=L; L=W; W=t; }   //output should sort by Lsolution[count].a = L ;solution[count].b = W;count++;} // combination 1111, four rectangles stand side by side void case1( const Rectangle* ori ){Rectangle r[4];int i, j,L, W;for( i=0; i<16; i++){   // i的低四位0000-1111 代表16种长宽搭配, 1代表旋转,0代表不转 L=W=0;setConbination( r, ori, i);for( j=0; j<4; j++){L += r[j].a;W = Max( W, r[j].b );}setSolution( L, W ); }}  // combination 112 , 6 kinds /* _ _ _|a|b|c|| | |_||_|_|d|*/void case2( const Rectangle* ori ){const int numCase=6;    //6 basic combinations, indictator[3][1] means rec indictator[3][1]int indicator[][4]={    // needs puting in the positong b of 3rd type.( 0123 means abcd ){0, 1, 2, 3},    {0, 2, 1, 3},    {0, 3, 1, 2},{1, 2, 0, 3},    {1, 3, 0, 2},    {2, 3, 0, 1},};int (*p)[4] = indicator;Rectangle r[4];int i,k,  L,W;for( k=0; k<numCase; k++){   for( i=0; i<16; i++){L=W=0;setConbination( r, ori, i);L = r[p[k][0]].a +  r[ p[k][1] ].a + Max( r[ p[k][2] ].a, r[ p[k][3] ].a );W = Max( r[p[k][0]].b, Max( r[p[k][1]].b, r[ p[k][2] ].b + r[ p[k][3] ].b ) );setSolution( L, W );}}}// 田字摆放,只有三种本质不同的组合(对角线), 注意重叠问题 /*dcab32  23  3101  01  02*/void case3( const Rectangle* ori ){const int numCase = 3;int indicator[][4]={ {0, 1, 2, 3},    {0, 1, 3, 2},    {0, 2, 1, 3},};Rectangle r[4];int i,k,  L,W;int a, b, c, d;for( k=0; k<numCase; k++){a = indicator[k][0];  b=indicator[k][1];  c = indicator[k][2];  d=indicator[k][3];for( i=0; i<16; i++){L=W=0;setConbination( r, ori, i);//Too simple! It is Buggy!! overlap will happen and compute a too small result. //L =  Max( r[].a + r[p[k][1]].a,   r[p[k][2]].a + r[p[k][3]].a );//W =  Max( r[p[k][0]].b + r[p[k][3]].b,   r[p[k][1]].b + r[p[k][2]].b );if( r[a].b > r[b].b ){  //奠基的两块分高低。 低的那块先填 L = r[a].a + Max( r[b].a, r[c].a);  // b比较矮,填上c块L = Max( L, r[d].a+r[c].a );// 填上d块 }else{L = r[b].a + Max( r[a].a, r[d].a);   //a比较矮,所以先填上d块。 L = Max( L, r[c].a+r[d].a );// 填上c块}W = Max( r[a].b+r[d].b,  r[c].b+r[b].b ); setSolution( L, W );//printf("## %d %d    %d %d %d %d %d\n", L, W, a, b, c, d, i); }}}/*C(2,4)*2 = 12ab 顺序有影响 , cd 没有  _ _ _| |d|c||a|_|_||_|b__|*/void case4( const Rectangle* ori ){const int numCase = 12; int indicator[][4]={ //indicator[k][0]  {0, 1, 2, 3},   {0, 2, 1, 3},   {0, 3, 1, 2},  {1, 0, 2, 3},   {2, 0, 1, 3},   {3, 0, 1, 2},{1, 2, 0, 3},   {1, 3, 0, 2},   {2, 3, 0, 1},{2, 1, 0, 3},   {3, 1, 0, 2},   {3, 2, 0, 1}, };int (*p)[4] = indicator;Rectangle r[4];int i,k,  L,W;for( k=0; k<numCase; k++){for( i=0; i<16; i++){L=W=0;setConbination( r, ori, i);if( r[p[k][1]].b < r[p[k][0]].b )   //a块比b矮 L = r[p[k][0]].a + Max( r[p[k][1]].a,  r[ p[k][2]].a+r[ p[k][3]].a );elseL = Max( r[p[k][0]].a + r[p[k][1]].a,  r[ p[k][2]].a+r[ p[k][3]].a );W = Max( r[p[k][0]].b,   r[p[k][1]].b + Max( r[p[k][2]].b, r[p[k][3]].b ) );setSolution( L, W );//printf("## %d %d    %d %d %d %d %d\n", L, W, p[k][0], p[k][1], p[k][2], p[k][3], i); }}}/* _ _ _|d|c|b||_|_|_||__a__|*/void case5( const Rectangle* ori ){const int numCase = 4; int indicator[][4]={ {0, 1, 2, 3},   {1, 0, 2, 3}, {2, 0, 1, 3},   {3, 0, 1, 2}, };int (*p)[4] = indicator;Rectangle r[4];int i,k,  L,W;for( k=0; k<numCase; k++){for( i=0; i<16; i++){L=W=0;setConbination( r, ori, i);L = Max( r[p[k][0]].a,  r[p[k][1]].a + r[ p[k][2]].a + r[ p[k][3]].a );W = r[p[k][0]].b + Max( r[p[k][1]].b,  Max( r[p[k][2]].b, r[p[k][3]].b ) );setSolution( L, W );}}}void show( FILE* STREAM ){fprintf( STREAM, "%d\n%d %d\n", minArea, solution[0].a, solution[0].b );int i;for( i=1; i<count; i++){if ( solution[i].a * solution[i].b != minArea ) break;if ( solution[i].a == solution[i-1].a) continue;fprintf( STREAM, "%d %d\n", solution[i].a, solution[i].b );}} int cmp( const void* x, const void* y){const Rectangle *xx = x, *yy=y;int s=xx->a * xx->b,t=yy->a * yy->b;if(  s>t )return 1;else if ( s<t )return -1;elsereturn (xx->a - yy->a);   //面积相同,长越小越好 }int main(){FILE *fin, *fout;fin = fopen("packrec.in", "r");fout = fopen("packrec.out", "w");Rectangle rec[4];int i=0;while( i!=4 ){if( fscanf( fin, "%d %d", &rec[i].a, &rec[i].b )!=2 ){perror(" fscanf error\n");exit(0);}i++; } case1(rec);case2(rec);case3(rec);case4(rec);case5(rec);qsort( solution, count, sizeof(solution[0]), cmp);show( fout );show( stdout );fclose(fin);fclose(fout);return 0;}

代码写得很渣的感觉,caseX() 函数的重复代码很多,r[ indicator[][] ]的使用也很难看。 注释也是一个问题,都不知道怎么才说的清楚。表达能力的问题...哎。

调试还算简单,注释掉4个caseX调用,就能逐个排查了。 田字的重叠处理当时觉得真心麻烦,可是写出来好像也没几行代码。。。

自己折腾的测试用例

3 5 
1 3 
2 4 
2 5 
//田字40

3 4
2 5
3 3
2 4
//田字中空排列 40


17 11 
16 20 
4 6 
13 19
//田字 840 


5 5
1 7
2 6
2 2
//54, 
 
5 5
1 7
2 6
1 2
//49



原创粉丝点击