【拓扑排序】洛谷 - P2741 [USACO4.4] 重叠的图像Frame Up

在这里插入图片描述

P2741 [USACO4.4] 重叠的图像Frame Up

标签

拓扑排序好题

题目描述

戳这里

看下面的五张 9 x 8 的图像 (别说有点像某的世界的传送门)

........   ........   ........   ........   .CCC....
EEEEEE..   ........   ........   ..BBBB..   .C.C....
E....E..   DDDDDD..   ........   ..B..B..   .C.C....
E....E..   D....D..   ........   ..B..B..   .CCC....
E....E..   D....D..   ....AAAA   ..B..B..   ........
E....E..   D....D..   ....A..A   ..BBBB..   ........
E....E..   DDDDDD..   ....A..A   ........   ........
E....E..   ........   ....AAAA   ........   ........
EEEEEE..   ........   ........   ........   ........

   1          2           3          4          5

现在,把这些图像按照 1—5 的编号从下到上重叠,第 1 张在最下面,第 5 张在最顶端。如果一张图像覆盖了另外一张图像,那么底下的图像的一部分就变得不可见了。我们得到下面的图像:

             .CCC....
             ECBCBB..
             DCBCDB..
             DCCC.B..
             D.B.ABAA
             D.BBBB.A
             DDDDAD.A
             E...AAAA
             EEEEEE..

对于这样一张图像,计算构成这张图像的矩形图像从底部到顶端堆叠的顺序。

下面是这道题目的规则:

矩形的边的宽度为 1 ,每条边的长度都不小于 3 。

矩形的每条边中,至少有一部分是可见的。注意,一个角同时属于两条边。

矩形用大写字母表示,并且每个矩形的表示符号都不相同。

AC艰难历程

  • 44 44 44 分:打了一个普普通通的宽搜(拓扑)。
    • 错误原因:眼瞎 没有看到题目中按字典序输出所有拓扑序……
  • 100 100 100 分:突然眼不瞎了把普普通通的宽搜改成了普普通通的深搜

就说我错的冤不冤吧。

题解

思路

且看下面这张图:

             ........
             ECBCBB..
             D....B..
             D....B..
             D....B..
             D....B..
             D....D..
             E....A..
             EEEEEE..

这些是在 E 这个框框上的字符,

表明这些字符的优先级都在 E 之上;

我们只需要在建 DAG 图时将 E 指向这些节点即可;

最后用 DFS 求出这个 DAG 图的所有拓扑序即可。

证明

代码

#include <bits/stdc++.h>
#define ios                  \
ios::sync_with_stdio(0); \
cin.tie(0);              \
cout.tie(0)
// #pragma GCC Typetimize(2)
#define int long long
#define pll pair<ll, ll>
#define pii pair<int, int>
#define il inline
#define p_q priority_queue
#define u_m unordered_map
#define r_g register
using namespace std;

int n, m;
char a[100][100];
vector<int> G[1000];

class Square {//一个框框,保存左上节点和右下节点
	public:
		int maxX, maxY;
		int minX, minY;
		Square() {
			minX = minY = INT_MAX;
		}
};
Square sq[1000];
int In[1000];
bool vis[1000];
p_q<string, vector<string>, greater<string>> Ans;
vector<char> ans;
void dfs(string tans) {//dfs求拓扑序
	if (tans.size() == ans.size()) {
		Ans.push(tans);
		return ;
	}
	for (int i = 0; i < ans.size(); i++) {
		if (In[ans[i]] == 0) {
			tans.push_back(ans[i]);
			for (auto x : G[ans[i]]) {
				In[x]--;
			}
			dfs(tans);
			tans.pop_back();
			for (auto x : G[ans[i]]) {
				In[x]++;
			}
		}
	}
}

signed main() {
	memset(In, 0xff, sizeof(In));
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> a[i][j];
		}
	}
	for (int i = 1; i <= n; i++) {//获取框框边界
		for (int j = 1; j <= m; j++) {
			sq[a[i][j]].minX = min(sq[a[i][j]].minX, i);
			sq[a[i][j]].maxX = max(sq[a[i][j]].maxX, i);
			sq[a[i][j]].minY = min(sq[a[i][j]].minY, j);
			sq[a[i][j]].maxY = max(sq[a[i][j]].maxY, j);
		}
	}
	for (char c = 'A'; c <= 'Z'; c++) {//建图,有些冗余,凑活着看扒
		memset(vis, true, sizeof(vis));
		if (!sq[c].maxX)
			continue;
		ans.push_back(c);
		for (int i = sq[c].minX; i <= sq[c].maxX; i++) {
			if (vis[a[i][sq[c].minY]]) {
				In[a[i][sq[c].minY]] += vis[a[i][sq[c].minY]];
				vis[a[i][sq[c].minY]] = false;
				G[c].push_back(a[i][sq[c].minY]);
			}
			if (vis[a[i][sq[c].maxY]]) {
				In[a[i][sq[c].maxY]] += vis[a[i][sq[c].maxY]];
				vis[a[i][sq[c].maxY]] = false;
				G[c].push_back(a[i][sq[c].maxY]);
			}
		}
		for (int i = sq[c].minY; i <= sq[c].maxY; i++) {
			if (vis[a[sq[c].minX][i]]) {
				In[a[sq[c].minX][i]] += vis[a[sq[c].minX][i]];
				vis[a[sq[c].minX][i]] = false;
				G[c].push_back(a[sq[c].minX][i]);
			}
			if (vis[a[sq[c].maxX][i]]) {
				In[a[sq[c].maxX][i]] += vis[a[sq[c].maxX][i]];
				vis[a[sq[c].maxX][i]] = false;
				G[c].push_back(a[sq[c].maxX][i]);
			}
		}
	}
	dfs("");
	while (Ans.size()) {//输出
		cout << Ans.top() << "\n";
		Ans.pop();
	}
	return 0;
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值