「Codeforces 1131D」Gourmet Choice
Description
美食家 Apple 先生是一家美食杂志的主编。他会用一个正整数来评价每一道菜。
美食家在第一天品尝第 $n$ 道菜,第二天品尝了 $m$ 道菜。他制作了一张 $n\times m$ 的表格,记录了他对菜肴的评价。如果第一套中的第 $i$ 道菜比第二套中的第 $j$ 道菜好,那么 $a_{i,j}$ 等于 `>`;如果要差,那么 $a_{i,j}$ 等于 `<`。菜肴可能同样美味,那么 $a_{i,j}$ 等于 `=`。
现在 Apple 先生想让你帮他评价每道菜。由于他是非常严格的,他会对菜肴进行评估,以便使用的最大整数尽可能小。但是 Apple 先生也很公平,如果 $a_{i,j}$ 为 `<`,那么给第一套中第 $i$ 道菜的评价一定小于第二套中第 $j$ 道菜。如果 $a_{i,j}$ 是 `>` 那么应该要更大。如果 $a_{i,j}$ 为 `=`,那么这两个数字要相等。
帮助 Apple 先生评价这两套中的每一道菜,使之符合他的感受,并满足最大数字尽可能小。如果有解则输出 `Yes` 和评价的数字;否则输出 `No`。
数据范围:$1\le n,m\le 10^3$。
首先我们把 =
给处理掉,把所有相等的点缩在一起。如果同一个并查集中的点有 <
或 >
关系显然无解。
此时我们得到了一个有向图,直接对它拓扑排序,如果有环则无解,否则输出方案。
const int N = 2e3 + 10;
vector<int>e[N];
string s[N];
int f[N], p[N], d[N];
int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 0; i < n; ++i) cin >> s[i];
for (int i = 0; i < n + m + 1; ++i) f[i] = i;
for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j)
if (s[i][j] == '=') {
int u = find(i), v = find(n + j);
f[u] = v;
}
for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j)
if (s[i][j] != '=') {
int u = find(i), v = find(n + j);
if (s[i][j] == '<') {
e[u].push_back(v);
d[v] += 1;
} else {
e[v].push_back(u);
d[u] += 1;
}
}
queue<int>q;
for (int i = 0; i < n + m; ++i) if (find(i) == i and !d[i])q.push(i), p[i] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : e[u])
if (!(--d[v])) {
q.push(v);
p[v] = p[u] + 1;
}
}
for (int i = 0; i < n + m; ++i) if (!(p[i] = p[find(i)])) {cout << "No\n"; return 0;}
cout << "Yes\n";
for (int i = 0; i < n; ++i) cout << p[i] << " \n"[i == n - 1];
for (int i = 0; i < m; ++i) cout << p[i + n] << " ";
}