DAG上的最长路与字典序最小的方案输出

解决一个关于矩形嵌套的问题,任务是选择最多矩形排成一行,使得每个矩形(除最后一个)能被下一个矩形嵌套。问题转化为求DAG上的最长路径,并输出字典序最小的序列。利用DAG的结构,可以通过邻接矩阵或链式前向星存储,并采用记忆化深度搜索求解。最优解的字典序最小方案可通过回溯深搜输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。如果有多解,矩形编号的字典序应尽量小。

很显然,这样的矩阵嵌套关系是一种大套小,而小不能套大的情形,所以说这里的嵌套都是单向的,矩阵间的嵌套也不可能出现循环的情形,综合起来看就是DAG(directed acyclic graph)有向无环图的一种。

DAG既然是一个图,那么肯定要有它的存储方案,邻接矩阵或是链式前向星。而遍历这个图就需要一些方式,比如说dfs这样的深度搜索算法,而记忆化深度搜索是动态规划算法的另一种表现形式,可以减少重复计算,降低复杂度。

所以对于这个题,建好图然后进行记忆化搜索就可以解出,如果要输出字典序最小方案,只需要从最优方案的终下标按照一定规律深搜回去并输出即可。

#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define reset(a, b) memset(a, b, sizeof(a))
const int INF = 0x3f3f3f3f;
using namespace std;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 2000;
inline ll lldcin()
{
	ll tmp = 0, si = 1;
	char c;
	c = getchar();
	while (c > '9' || c < '0')
	{
		if (c == '-')
			si = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		tmp = tmp * 10 + c - '0';
		c = getchar();
	}
	return si * tmp;
}
///Untersee Boot IXD2(1942)
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
/**Last Remote**/
struct rec
{
	ll length, width;
}recs[124124];
ll relationships[1200][1200], dp[1200];
ll dfs(ll current, ll limit)//记忆化搜索的函数
{
	ll &tmp = dp[current];//使用引用符号是为了变更tmp值的同时改变dp[current]的值
	if (tmp)
		return tmp;
	else
	{
		tmp = 1;
		for (int i = 1; i <= limit; i++)
		{
			if (relationships[current][i])//如果存在嵌套关系,继续深搜。
				tmp = max(tmp, dfs(i, limit) + 1);//取最优解
		}
		return	tmp;
	}
}
void print_ans(ll current, ll limit)
{
	for (int i = 1; i <= limit; i++)
	{
		if (relationships[current][i] && dp[current] == dp[i] + 1)//如果存在嵌套关系并且i矩形的嵌套关系可以由现状直接推出
		{
			print_ans(i, limit);
			break;
		}
	}
	cout << current << " ";//回溯后输出
}
int DETERMINATION()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		ll tmp1, tmp2;
		cin >> tmp1 >> tmp2;
		if (tmp1 < tmp2)
			swap(tmp1, tmp2);
		recs[i].length = tmp1, recs[i].width = tmp2;//直接把矩形旋转到同样的长宽摆放方式
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)//建图,这个必须双向判断,因为第一个if只能判断单减样例,第二个if只能判断单增样例。
		{
			if (recs[i].length > recs[j].length&&recs[i].width > recs[j].width)
				relationships[i][j] = 1;
			else if (recs[i].length < recs[j].length&&recs[i].width < recs[j].width)
				relationships[j][i] = 1;
		}
	}
	//for (int i = 1; i <= n; i++)
	//{
	//	for (int j = 1; j <= n; j++)
	//		cout << relationships[i][j] << " ";
	//	cout << endl;
	//}
	ll ans = 0, mk = 0;
	for (int i = 1; i <= n; i++)
	{
		ll t = dfs(i, n);
		if (t > ans)
		{
			ans = t;
			mk = i;
		}
	}
	print_ans(mk, n);
	cout << endl;
	cout << ans << endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值