题目很简单,大约是这个意思:
有n个矩形,每个矩形可以用两个整数a,b描述,表示它的长和宽。你的任务就是选出尽量多的矩形排成一行,使得除最后一个以外,每个矩形都可以嵌套在下一个矩形内。
算法竞赛这本书提供了这样两段代码:
int dp(int i)
{
int& ans = d[i];
if(ans > 0) return ans;
ans = 1;
for(int j = 1; j <= n; j++)
if(G[i][j]) ans >?= dp(j) + 1;
return ans;
}
void printf_ans(int i)
{
printf("%d ", i);
for(int j = 1; j <= n; j++) if(G[i][j] && d[i] == d[j] + 1)
{
print_ans(j);
break;
}
}
由于我个人的代码是C语言的,所以与其不同。对于int& ans = d[i],让我纠结了很久,这本书上是这解释其目的:“这里用到了一个技巧:为表明d[i]申明一个引用ans。这样,任何对ans的读写实际上都是在对d[i]进行。”但我一直编译不通过,最后只得把int& ans = d[i]这句话去掉了。也许人品不好吧,技巧都用不了。。。对于ans >?= dp(j) + 1这样语句还是要翻译一下的,意思也就是ans等于ans与dp(j) + 1之间的较大的那一个。好吧,最后还有第二段代码中输出语句,我认为应放在最后,这样才保证是按由小到大输出矩形序号的。这本书上的分析还是比较抽象的,下面我来让它具象具象吧。
首先用一个结构体rect保存矩形(rect.a、rect.b保存其长、宽,不一定是一一对应的),申明一个二维数组G[X][X](G[i][j]表示第j个矩形是否能被第i个矩形接纳),一个一维数组d[X](d[i]记录的就是第i个矩形能娶到的矩形个数,初始为0)。再按序从1-n,枚举矩形i,再看1-n里面每一个矩形j是否愿意嫁给这个矩形,若愿意G[i]j[j] = 1,否则G[i]j[j] = 0。这样所有矩形i(1-n)都有清楚1-n这里面矩形是否愿嫁给自己(此处复杂度为n^2);准备工作做好之后就开始传说中的dp了。。。dp(i)其实就是简单的从1-n浏览一遍,看是否有个j愿意嫁给他,即G[i][j]是否为1,但i不傻,因为i娶了j之后,以前嫁给j的矩形就会变成i的小矩形,所以i会娶已经拥有矩形最多的j。所以i首先判断自己现在的状况和娶了j之后的状况d[i] = max(d[i], dp(j) + 1) 在1-n循环之后,d[i]就是最大的了。此处还用到记忆化搜索,就是在dp之初,判断一下d[i]是否为0,因为默认是0的,若不是,就直接返回d[i],避免了重复计算。
代码如下:
#include<stdio.h>
#include<string.h>
#define MAXN 1000
int G[MAXN][MAXN], d[MAXN], n;
int max(int x, int y){
return x > y ? x : y;
}
struct rectangle{ //记录矩形
int a, b;
}rect[MAXN];
int dp(int i){
int j;
if(d[i] != 0) //记忆化搜索,如果已经计算过d[i]就直接放回d[i]
return d[i];
d[i]++; //如果d[i] = 0 就自增一下,因为会计算矩形自身的
for(j = 1; j <= n; j++) if(G[i][j]) //如果i能包含j
d[i] = max(d[i], dp(j) + 1); //找d[i]最大值
return d[i];
}
void printf_ans(int i){
int j;
for(j = 1; j <= n; j++) if(G[i][j] && d[i] == d[j] + 1){
printf_ans(j);
break;
}
printf("%d ", i); //因为d[i]中的i是最大的矩形的序号,所以应该最后输出,所以输出语句至于最后
}
int main(){
int i, t, j, mmax;
scanf("%d", &t);
while(t--){
mmax = 0;
scanf("%d", &n);
for(i = 1; i <= n; i++)
scanf("%d%d", &rect[i].a, &rect[i].b);
memset(G, 0, sizeof(G));
memset(d, 0, sizeof(d));
for(i = 1; i <= n; i++){
for(j = 1; j <= n; j++)
if((rect[i].a > rect[j].a && rect[i].b > rect[j].b) || (rect[i].a > rect[j].b && rect[i].b > rect[j].a)){//考虑到矩形长宽的变换
G[i][j] = 1;
//printf("G[%d][%d] = %d\n", i, j, G[i][j]); //调试时看矩形包含关系是否正确
}
}
for(i = 1; i <= n; i++){ //找出最大的矩形和其包含矩形的数目
if(mmax < dp(i)){
j = i; //借用j记录最大d[i]的序号
mmax = dp(i);
}
}
printf("%d\n", mmax);
printf_ans(j);
printf("\n");
}
return 0;
}
以上“具象”的比喻文字绝无恶意,纯属简化题意。。。请勿误解。。。