【算法随笔:NYOJ-嵌套矩形】(Dp on DAG | DAG 最长路 | 记忆化搜索 | 字典序排序)

该篇文章介绍了如何通过记忆化搜索和二维矩阵操作解决一个有趣的问题:给定一组矩形,选择尽可能多的矩形按顺序排列,使得除了最后一个,每个矩形都能嵌套在其后一个矩形内。作者提供了C++代码实现这一过程并按照字典序输出结果。

有n个矩形,每个矩形可以用两个整数a、b描述,表示它的长和宽。

矩形X(a,b)可以嵌套在矩形Y(c,d)中,当且仅当 a<c,b<d,或者b<c,a<d

选出尽可能多的矩形排成一行,使得除了最后一个之外,每一个矩形都可以嵌套在下一个矩形内。
输出这一排矩形,如果有多个矩形序列满足要求,按照字典序的顺序输出

这是一个很经典,也很有意思的问题,下面是ac代码

 

#include <cstdio>
#include <array>
#include <algorithm>
#include <bitset>

constexpr std::size_t NN { 1001U };
//题目给出矩形最大数目

static int N {};

std::array<int, NN> record{ {0,} };
//记忆化搜索dp的记录数组
std::array<int, NN> route;
//路径记录(按照矩形下标字典序排序)
int counter {};

std::bitset<NN * NN> nestable;
//行优先扁平化一个二维矩阵,记录每两个矩形i,j是否形成嵌套关系

typedef struct{ int w, h; } rectangle;
std::array<rectangle, NN> rectangles;
//矩形

inline
int const __index__(int i, int j)
{ return (i * N + j); }
//还原二维矩阵下标

inline
bool const __judge__(int i, int j){
	return (std::min(rectangles[j].w, rectangles[i].h)
			 < std::min(rectangles[i].w, rectangles[i].h));
}
//min(a, b) < min(x, y)等价于
//(a < x && b < y) || (b < x && a < y)

int dfs(int index){
	auto& d = record[index];
	if(d) return d;
	for(int Ot {}; Ot < N; ++Ot)
		if(nestable[__index__(index, Ot)]
		||(nestable[__index__(index, Ot)]
		= __judge__(index, Ot)))//if的副作用
			d = std::max(d, dfs(Ot));
	return ++d;
}
//记忆化搜索(标准的模板。。)

void collect_by_order(int index){
	route[counter++] = index;
	for(int Ot {}; Ot < N; ++Ot)
		if(nestable[__index__(index, Ot)]
		&& record[index] == record[Ot] + 1){
			collect_by_order(Ot);
			break;
		}
}
//按照字典序收集答案序列

int main(void){
	std::scanf("%d", &N);

	for(int i {}; i < N; ++i)
		std::scanf("%d%d",
		&rectangles[i].w,
		&rectangles[i].h);

	for(int i {}; i < N; ++i)
		dfs(i);//每个都dfs试探

	int start {};
	for(int i {}; i < N; ++i)
		if(record[start] < record[i])
			start = i;
    //以rectangle[start]为首个矩形得到最长dag路

	collect_by_order(start);//字典序收集,方便输出

	for(int i {}; i < counter; ++i)
		std::printf("%d%c", route[i],
		" \n"[(std::size_t)(i == counter - 1)]);//明文字符串

	return 0;
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XNB's Not a Beginner

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值