[USACO] Packing Rectangles

最近一个月一直在忙GSoC,没时间做题,也就没更新,很惭愧。。。

本想速战速决来个开门红,结果遇到了传说中usaco第一道杀脑细胞的题,用了一天时间外加网上提示才搞定,真是失败!

这道题想法倒是不难,枚举所有的可能情况,6种基本布局,每个布局有4!种放矩形的可能,又因为矩形的位置固定后可以旋转,故每种放置又有2*2*2*2中旋转可能,对每一种可能找到它的外围矩形,然后更新最小面积值。

最开始我理解题目出现错误,我以为四个矩形必须拼成一个完整的大矩形(例子给的四个矩形和输出就可以,被误导了!),结果有一组数据过不去。[4,5][4,5][4,5][16,1]这四个矩形无法拼出一个完整的矩形,后来才发现找到能够包围那6个布局的最小矩形就可以了。杯具啊~~

然后最大的问题出现在最后一种布局求外围矩形的宽时,其又分了五种情况,这地方我没想通,看了别人的题解。

链接地址:http://starforever.blog.hexun.com/2097115_d.html

作者把最后一种布局细分为五种情况,对此我并不太认同。其实,第一种和第四种划分是可以通过第三种布局通过旋转,反射得到的,所以三种划分其实就够了。

另外忘了说明,第四种布局和第五种布局在编程时可以放在一起处理,这个也看提示了。。。

下图是我对6种布局进行的标记,与我的代码相对应。


/* ID: LANG: C TASK: packrec */ #include #include #include #define WI(x) (x & 0x08) >> 3 #define WJ(x) (x & 0x04) >> 2 #define WK(x) (x & 0x02) >> 1 #define WL(x) (x & 0x01) typedef struct rect_t { int side[2]; }rect_t; rect_t in[4], out[6]; int idx; int area; int max2(int a, int b) { return a >= b ? a : b; } int max3(int a, int b, int c) { return max2(max2(a, b), c); } int max4(int a, int b, int c, int d) { return max2(max3(a, b, c), d); } void check(int w, int h) { //更新最小面积,同时记录边长,使用插入排序法 int min = w * h; int i, j; rect_t tmp; if(min > area) return; else if(min == area) { tmp.side[0] = w < h ? w : h; tmp.side[1] = w > h ? w : h; for(i = 0; i < idx; i++) { if(tmp.side[0] == out[i].side[0]) return; if(tmp.side[0] < out[i].side[0]) { for(j = idx; j > i; j--) out[j] = out[j - 1]; out[i] = tmp; idx++; return; } } out[idx++] = tmp; } else { area = min; idx = 0; out[idx].side[0] = w < h ? w : h; out[idx++].side[1] = w > h ? w : h; } } int main() { FILE *fin = fopen("packrec.in", "r"); FILE *fout = fopen("packrec.out", "w"); int i, j, k, l; unsigned char q; memset((char *)in, 0, sizeof(rect_t) * 4); memset((char *)out, 0, sizeof(rect_t) * 6); for(i = 0; i < 4; i++) { fscanf(fin, "%d %d", &in[i].side[0], &in[i].side[1]); } idx = 0; area = ~0x80000000; //area先取最大值 for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(i == j) continue; for(k = 0; k < 4; k++) { if(k == i || k == j) continue; l = 6 - i - j - k; //用i, j, k, l标识一个排列 for(q = 0; q < 16; q++) { //位置固定时,每个位置上的矩形旋转,一共有16中可能 int w[4], h[4], wmin, hmin; w[0] = in[i].side[WI(q)]; //因为q在0~15之间,恰好可以用4bit表示,每个bit表示一个矩形的旋转 h[0] = in[i].side[1 - (WI(q))]; w[1] = in[j].side[WJ(q)]; h[1] = in[j].side[1 - (WJ(q))]; w[2] = in[k].side[WK(q)]; h[2] = in[k].side[1 - (WK(q))]; w[3] = in[l].side[WL(q)]; h[3] = in[l].side[1 - WL(q)]; wmin = w[0] + w[1] + w[2] + w[3]; //第1种布局 hmin = max4(h[0], h[1], h[2], h[3]); check(wmin, hmin); wmin = max2(w[3], w[0]+w[1]+w[2]); //第2种布局 hmin = max3(h[0], h[1], h[2]) + h[3]; check(wmin, hmin); wmin = max2(w[0]+w[1], w[2]) + w[3]; //第3种布局 hmin = max3(h[0]+h[2], h[1]+h[2], h[3]); check(wmin, hmin); wmin = max2(w[0], w[1]) + w[2] + w[3]; //第4,5种布局 hmin = max3(h[0]+h[1], h[2], h[3]); check(wmin, hmin); if(h[2] == h[3]) //第6种布局 wmin = max2(w[0]+w[1], w[2]+w[3]); else if(h[2] > h[3] && h[2] < h[3]+h[1]) wmin = max3(w[2]+w[3], w[2]+w[1], w[0]+w[1]); else if(h[2] < h[3] && h[2]+h[0] > h[3]) wmin = max3(w[3]+w[2], w[3]+w[0], w[0]+w[1]); hmin = max2(h[0]+h[2], h[1]+h[3]); check(wmin, hmin); } } } } fprintf(fout, "%d/n", area); for(i = 0; i < idx; i++) { fprintf(fout, "%d %d/n", out[i].side[0], out[i].side[1]); } exit(0); }

 


 

分析的解法没看,最近有点浮躁,估计也得花点时间,以后再说吧~~

你可能感兴趣的:([USACO] Packing Rectangles)