LeeCode 1632 拓扑序 + 并查集

题意

传送门 LeeCode 1632. 矩阵转换后的秩

题解

考虑同行同列的数字较小值向较大值连边,得到一个 D A G DAG DAG,则问题转化为求解拓扑序。对于同行或列相同值元素,它们秩相同,若相互连边,图中会出现环,那么将其看做一个点;用并查集维护一个同行或列相同值元素构成的连通分量,用根节点代表原图各点进行连边。

class Solution
{
#define maxn 500
    typedef pair<int, int> P;

public:
    int n, m, deg[maxn * maxn], rec[maxn * maxn], par[maxn * maxn], rnk[maxn * maxn];
    P r[maxn][maxn], c[maxn][maxn];
    vector<int> G[maxn * maxn];
    void init(int n)
    {
        for (int i = 0; i < n; ++i)
            par[i] = i, rnk[i] = 0;
    }
    int find(int x)
    {
        return par[x] == x ? x : (par[x] = find(par[x]));
    }
    void unite(int x, int y)
    {
        x = find(x), y = find(y);
        if (x == y)
            return;
        if (rnk[x] > rnk[y])
            par[y] = x;
        else
        {
            par[x] = y;
            if (rnk[x] == rnk[y])
                ++rnk[y];
        }
    }
    vector<vector<int>> matrixRankTransform(vector<vector<int>> &matrix)
    {
        n = matrix.size(), m = matrix[0].size();
        int sz = n * m;
        init(sz);
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
                r[i][j] = P(matrix[i][j], i * m + j);
            sort(r[i], r[i] + m);
            for (int j = 1; j < m; ++j)
            {
                if (r[i][j - 1].first == r[i][j].first)
                    unite(r[i][j - 1].second, r[i][j].second);
            }
        }
        for (int j = 0; j < m; ++j)
        {
            for (int i = 0; i < n; ++i)
                c[j][i] = P(matrix[i][j], i * m + j);
            sort(c[j], c[j] + n);
            for (int i = 1; i < n; ++i)
            {
                if (c[j][i - 1].first == c[j][i].first)
                    unite(c[j][i - 1].second, c[j][i].second);
            }
        }
        for (int i = 0; i < n; ++i)
            for (int j = 1; j < m; ++j)
                if (r[i][j - 1].first != r[i][j].first)
                {
                    int u = find(r[i][j - 1].second), v = find(r[i][j].second);
                    ++deg[v];
                    G[u].push_back(v);
                }
        for (int j = 0; j < m; ++j)
            for (int i = 1; i < n; ++i)
                if (c[j][i - 1].first != c[j][i].first)
                {
                    int u = find(c[j][i - 1].second), v = find(c[j][i].second);
                    ++deg[v];
                    G[u].push_back(v);
                }
        queue<int> q;
        for (int i = 0; i < sz; ++i)
        {
            if (!deg[i] && par[i] == i)
                q.push(i);
        }
        while (!q.empty())
        {
            int v = q.front();
            q.pop();
            for (int i = 0; i < G[v].size(); ++i)
            {
                int u = G[v][i];
                rec[u] = max(rec[u], rec[v] + 1);
                if (--deg[u] == 0)
                    q.push(u);
            }
        }
        vector<vector<int>> res(n, vector<int>(m));
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                res[i][j] = rec[find(i * m + j)] + 1;
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值