题解
题目大意,一个人第一天吃了n份食物第二天吃了m份食物,给你一个n*m的矩阵表示第一天的某份食物和第二天的某份食物哪个更好吃或一样好吃,要求你对每个食物进行打分满足所给矩阵。
首先使用并查集将一样好吃的情况缩为一个点进行处理,如果a>b则b<—a建一条单向边,如果a<b则a—>b建一条单向边。
初始入度为0的点评分设置为1,使用拓扑排序将当前点指向的点评分对当前点评分+1取max,并不断删去边。
如果到最后还有点没有评分则说明有环,无解。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e3 + 10;
char g[N][N];
vector<int> e[N];
int a[N], v[N], d[N];
int find(int x)
{
return v[x] == x ? x : v[x] = find(v[x]);
}
void join(int x, int y)
{
v[find(x)] = find(y);
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n, m;
cin >> n >> m;
for (int i = 1; i <= n + m; i++) //1~i为第一天 n+1~n+m为第二天
v[i] = i;
for (int i = 1; i <= n; i++)
{
scanf("%s", g[i] + 1);
for (int j = 1; j <= m; j++)
if (g[i][j] == '=')
join(i, n + j); //等号合并为同一个点
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (g[i][j] == '<')
e[find(i)].push_back(find(j + n)), d[find(j + n)]++;
else if (g[i][j] == '>')
e[find(j + n)].push_back(find(i)), d[find(i)]++;
queue<int> q;
for (int i = 1; i <= n + m; i++)
if (find(i) == i && !d[i]) //是根节点且入度为0
q.push(i), a[i] = 1; //初始节点为1
while (!q.empty())
{
int f = q.front(); q.pop();
for (int i : e[f])
{
a[i] = max(a[i], a[f] + 1);
d[i]--;
if (!d[i]) q.push(i);
}
}
for (int i = 1; i <= n + m; i++)
if (find(i) == i && d[i]) //还有节点未处理
cout << "No" << endl, exit(0);
cout << "Yes" << endl;
for (int i = 1; i <= n; i++)
printf("%d ", a[find(i)]);
cout << endl;
for (int i = n + 1; i <= n + m; i++)
printf("%d ", a[find(i)]);
cout << endl;
return 0;
}