USACO Section 1.4 packrec

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


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

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

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

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

      )

         

1  并排,上方没其他矩形2 这两种可以合并,矩形上方仅有1个矩形矩形上方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 rectangle
int count=0;			  // number of possible solution
int 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 L
	solution[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 );
		}
	}
}


// 田字摆放,只有三种本质不同的组合(对角线), 注意重叠问题 
/*
	dc
	ab
	32  23  31
	01  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 = 12
	ab 顺序有影响 , 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 );
			else
				L = 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;
	else			return (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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值