//毕竟我不是dd牛,USACO的题解也不可能一句话带过的……
题目链接:http://cerberus.delos.com:790/usacoprob2?a=pWvHFwGsTb2&S=packrec
题目大意就是给定四个矩形,让你找到一个面积最小的矩形使得这四个矩形都能放进去(不能重叠),要求输出最小矩形的面积以及长宽(可能有多个矩形都具有最小面积)。
既然题目里已经给了六种模型(第4种和第5种其实是一样的),那就一种一种写吧。
没有什么特别的算法,就是搜索。每个矩形都可以横放或者竖放,所以用一个dir[4]数组表示4个矩形的放置方式:0表示竖放,即较小边在下;1表示横放,即较大边在下。
为了方便,我还把“找到新矩形并判断面积是否最小并记录其边长”的功能写成了一个函数newans(x, y),具体代码见下:
void newans(int x, int y) { if (x*y <= mina) { if (x*y < mina){ mina = x*y; memset(minx, 0, sizeof(minx)); } int v = min(x, mina/x); if (!minx[mina/v]) { minx[v] = 1; } }}
解释一下这种记录结果的方式:
x,y自然是新生成的矩形的长宽;mina即最小面积(min area);minx[201]记录了最小矩形的较短边(或者说宽),即当minx[w]==1时,说明有一个最小矩形的宽为w。如果生成了更小的矩形,那么重置minx,更新mina。而且题目要求先输出矩形的较短边,再输出较长边,那么这里为了省事,只记录较短边。(即如果生成一个5 8的最小矩形,mina=40,minx[5]=1,minx[8]=0。)
然后回溯吧。(接下来这些描述中,宽是指放在下面的那条边,而不是指较小边)
Case 1:
不用说,简单回溯就好了。有点全排列的感觉:在dir数组每个位置上放1或者0。当然也可以用二进制数的方法:从0枚举到15,用位运算取出每位上的数。
Case 2:
感觉自己用了很奇葩的方法:在主函数里用循环来枚举放在下面的矩形,然后再回溯搜索dir的可能情况。可能解释不太清楚,具体可以看代码。那么这个新生成的矩形的长取上面三个矩形的宽之和与下面这个矩形的长的较大者,宽取上面三个矩形的长的最大值+下面这个矩形的宽。
Case3/4/5:
都类似于Case 2,只是需要确定的矩形个数变成了两个。
Case 6:
最让人头疼的一种情况,当初也是因为看到这种情况才放弃这道题的(所以一直拖了半年)。冷静分析之后,按照我之前处理其他情况的方法,可以先确定四个矩形分别在哪个位置,回溯搜索dir。只是关于重叠的问题需要大量的判断。比如我遇到左上矩形的宽大于左下矩形的宽时,就直接放弃这种情况(不用担心遗漏,因为一定有一种情况和它是对称的,即这种情况下的左上矩形成了那种情况的右上矩形)。具体的判断比较复杂,直接看代码吧。
提交了11次才通过。囧。第1次提交的时候,第1组数据(样例)都没有通过,这让我十分苦恼啊,在自己的电脑上完全没问题啊。然后,看了下USACO的FAQ,决定用cerr把中间结果输出来,于是我就在USACO的测评机上调试自己的代码……好吧,几次调试之后,发现是有个变量忘了初始化了,而自己的电脑上估计已经初始化成0了。所以说编译器如果太智能也不行,这种低级的错误都没法让写程序的人发现。后来的几次提交发现了Case 6的处理还有一些问题,于是不断修改。然后AC。
总共用了将近4小时吧,从8点一直做到11点多才AC。200+行,总代码长度4K+(不得不说是我写得太复杂了),最后0ms通过。爽。
代码如下:
#include#include #include #include #define cin fin#define cout fout#define INF 50000using namespace std;ifstream fin("packrec.in");ofstream fout("packrec.out");int rect[4][2]; // 0:x 1:y x<=yint minx[201], mina = INF;int dir[4]; // 0:vertical 1:horizontalvoid newans(int x, int y) { if (x*y <= mina) { if (x*y < mina){ mina = x*y; memset(minx, 0, sizeof(minx)); } int v = min(x, mina/x); if (!minx[mina/v]) { minx[v] = 1; } }}void pack1(int step) { if (step == 4) { int cy = 0, cx = 0, i, x[4], y[4]; for (i=0; i<4; i++) { x[i] = rect[i][dir[i]]; y[i] = rect[i][1-dir[i]]; if (y[i] > cy) { cy = y[i]; } cx += x[i]; } newans(cx, cy); } else { dir[step] = 0; pack1(step+1); dir[step] = 1; pack1(step+1); }}void pack2(int btm, int step) { if (step == 4) { int cy = 0, cx = 0, i, x[4], y[4]; for (i=0; i<4; i++) { x[i] = rect[i][dir[i]]; y[i] = rect[i][1-dir[i]]; } for (i=0; i<4; i++) { if (i != btm) { if (y[i] > cy) { cy = y[i]; } cx += x[i]; } } newans(max(cx, y[btm]), cy+x[btm]); } else { dir[step] = 0; pack2(btm, step+1); dir[step] = 1; pack2(btm, step+1); }}void pack3(int btm, int rgt, int step) { if (step == 4) { int cy = 0, cx = 0, i, x[4], y[4]; for (i=0; i<4; i++) { x[i] = rect[i][dir[i]]; y[i] = rect[i][1-dir[i]]; } for (i=0; i<4; i++) { if (i!=btm && i!=rgt) { if (y[i] > cy) { cy = y[i]; } cx += x[i]; } } newans(max(cx, y[btm])+x[rgt], max(cy+x[btm], y[rgt])); } else { dir[step] = 0; pack3(btm, rgt, step+1); dir[step] = 1; pack3(btm, rgt, step+1); }}int pack4(int top, int btm, int step) { if (step == 4) { int cy, cx, i, x[4], y[4]; for (i=0; i<4; i++) { x[i] = rect[i][dir[i]]; y[i] = rect[i][1-dir[i]]; } cy = y[top] + y[btm]; cx = max(x[top], x[btm]); for (i=0; i<4; i++) { if (i!=btm && i!=top) { if (y[i] > cy) { cy = y[i]; } cx += x[i]; } } newans(cx, cy); } else { dir[step] = 0; pack4(top, btm, step+1); dir[step] = 1; pack4(top, btm, step+1); }}void pack6(int lft, int rgt, int top, int step) { if (step == 4) { int cx, cy, i, last, x[4], y[4]; for (i=0; i<4; i++) { x[i] = rect[i][dir[i]]; y[i] = rect[i][1-dir[i]]; if (i!=lft && i!=rgt && i!=top) { last = i; } } if (x[top] > x[lft]) { return; } cx = x[lft] + x[rgt]; cy = y[lft] + y[top]; if (cy > y[rgt]) { if (y[lft] < y[rgt]) { if (x[top]+x[last] <= cx) { cy = max(cy, y[rgt]+y[last]); } else { return; } } else { if (x[top]+x[last] <= cx) { cy = max(cy, y[lft]+y[last]); } else { return; } } } else { if (x[last]+x[rgt] <= cx) { cy = max(cy+y[last], y[rgt]); } else { return; } } newans(cx, cy); } else { dir[step] = 0; pack6(lft, rgt, top, step+1); dir[step] = 1; pack6(lft, rgt, top, step+1); }}int main() { int i, j, k; for (i=0; i<4; i++) { cin >> rect[i][0] >> rect[i][1]; if (rect[i][0] > rect[i][1]) { rect[i][1] += rect[i][0]; rect[i][0] = rect[i][1] - rect[i][0]; rect[i][1] -= rect[i][0]; } } pack1(0); for (i=0; i<4; i++) { pack2(i, 0); } for (i=0; i<4; i++) { for (j=0; j<4; j++) { if (i != j) { pack3(i, j, 0); } } } for (i=0; i<4; i++) { for (j=0; j<4; j++) { if (i != j) { pack4(i, j, 0); } } } for (i=0; i<4; i++) { for (j=0; j<4; j++) { for (k=0; k<4; k++) { if (i != j && j != k && i != k) { pack6(i, j, k, 0); } } } } cout << mina << endl; for (i=1; i<201; i++) { if (minx[i]) { cout << i << ' ' << mina/i << endl; } } return 0;}