Codeforces Round #541 (Div. 2) D. Gourmet choice (并查集缩点)

题目链接:https://codeforces.com/problemset/problem/1131/D

题目大意:

       共有n+m道菜,美食家知道这些菜两两之间的好坏,问你这m+n道菜每一道菜的分数是多少?最差的菜分数是1,依照好的程度向上+1。两道菜比较之间好坏分数可能相同。如果不存在答案输出No,否则输出Yes和每一道菜的分数。

解题思路:

       由于有相同分数的菜,我们可以先用并查集维护分数相同的菜种,再检查会不会有冲突的情况(同一个unit但是却有大小之分),有冲突直接输出No,否则进入dfs遍历图。注意要缩点,只需要每一个unit的根建图。缩点之后图是单向的,如果图有环也输出No。这里要用记忆化搜索,更新过的点就不必再dfs了,否则超时。最后根据unit的根更新所有点的答案就可以了。

代码如下:

# include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 5;
int n, m;
int ans[maxn*2], indeg[maxn*2], vis[maxn*2], fa[maxn*2];
char s[maxn][maxn];
vector <int> G[maxn*2];

int Find(int v){
    return fa[v] == v ? v : fa[v] = Find(fa[v]);
}

void unite(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x == y)    return ;

    fa[y] = x;
}

bool same(int x, int y){
    return Find(x) == Find(y);
}

int max(int x, int y){
    return x > y ? x : y;
}

int dfs(int v){
    if(ans[v] != 1) return ans[v];
    if(vis[v]){  //形成了一个环
        cout << "No" << endl;
        exit(0);
    }

    vis[v] = 1;
    for(int i = 0; i < G[v].size(); ++i){
        int w = G[v][i];
        if(!same(v, w))    ans[v] = max(ans[v], dfs(w)+1);  //不相同,向上+1
        else    ans[v] = max(ans[v], dfs(w));   //相同不加
    }
    vis[v] = 0;
    return ans[v];
}

int main(){
    std::ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 0; i <= n+m; ++i)   fa[i] = i;
    for(int i = 0; i <= m+n; ++i)   ans[i] = 1;

    for(int i = 1; i <= n; ++i)
        cin >> s[i]+1;

    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= m; ++j)
        if(s[i][j] == '=')  unite(i, j+n);

    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= m; ++j){
            if(s[i][j] == '>'){
                int fx = Find(i), fy = Find(j+n);
                if(fx == fy){
                    cout << "No" << endl;
                    return 0;
                }
                G[fx].push_back(fy);   //并查集缩点,再记忆化dfs
                indeg[fy]++;
            }
            else if(s[i][j] == '<'){
                int fx = Find(i), fy = Find(j+n);
                if(fx == fy){
                    cout << "No" << endl;
                    return 0;
                }
                G[fy].push_back(fx);
                indeg[fx]++;
            }
            else{
                G[i].push_back(j+n);
                indeg[j+n]++;
                unite(i, j+n);
            }
        }

    for(int i = 1; i <= n+m; ++i)
        if(indeg[i] == 0 && G[i].size())
            dfs(i);

    for(int v = 1; v <= m+n; ++v){
        int fa = Find(v);
        ans[v] = ans[fa];
    }

    for(int v = 1; v <= m+n; ++v){
        for(auto w: G[v]){
            if((same(v, w) && ans[v] != ans[w]) || (!same(v, w) && ans[v] <= ans[w])){
                cout << "No" << endl;
                return 0;
            }
        }
    }

    cout << "Yes" << endl;
    for(int i = 1; i <= n; ++i)
        cout << ans[i] << ' ';
    cout << endl;
    for(int i = n+1; i <= m+n; ++i)
        cout << ans[i] << ' ';
    cout << endl;

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值