codeforces 1131D 题解~(并查集/tarjan缩点+拓扑排序)

题意:有个人吃了两天饭,第一天吃了n个菜,第二天吃了m个菜,然后给出一个矩阵,矩阵上第i行第j列对应着第1天的第i道菜和第二天的第j道菜的关系。

有n*m个大小关系
d       e
a      >       <
b      >       >
c      =       <
表示 a>d,a<e,b>d,b>e,c=d,c<e
找出一种所有数字最小的可行方案
思路:大小关系可以用拓扑排序来解决。如果有a<b的关系,那么就连一条单向边a->b。最小的数字是1,一开始所有入度是0的点都作为1。对于边a->b,b的答案是a的答案+1。等于关系:可以用并查集,本质是把所有相同关系的点缩成一个点,作拓扑的时候只对那些父亲是自己的点,最后其它点的答案就是它父亲的答案。
 

#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
using namespace std;
const int N=2e3+5;
int n,m,book[N];
char e[N][N];
int val[N];
int f[N];
vector<int>s[N];
queue<int>q;
Init()
{
    for(int i=1;i<=n+m;i++)
        f[i]=i;
}
int getf(int x) {
	return f[x] == x ? x : f[x] = getf(f[x]);
}
void merge(int a,int b) {
	int fa = getf(a);
	int fb = getf(b);
	if(fa != fb) {
		f[fa] = fb;
	}
}
void add(int u,int v) {
	s[u].push_back(v);
	book[v] ++;
}
int main()
{
    cin>>n>>m;
   scanf("%d %d",&n,&m);
	for(int i=1; i<=n+m;i++) f[i] = i;
	for(int i=1; i<=n; i++) scanf("%s",e[i]+1);
	for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) {
			if(e[i][j] == '=') {
				merge(i,n+j);
			}
		}
	for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) {
			if(e[i][j] == '>') add(getf(j+n),getf(i));
			if(e[i][j] == '<') add(getf(i),getf(j+n));
		}
	for(int i=1; i<=n+m; i++) if(!book[i] && i == getf(i)) {
			q.push(i);
			val[i] = 1;
		}
    while(!q.empty())
    {
        int temp=q.front();
        q.pop();
        for(int i=0;i<s[temp].size();i++)
        {
            int t=s[temp][i];
            if(val[t])
                continue;
            book[t]--;
            if(!book[t])
            {
                val[t]=val[temp]+1;
                q.push(t);
            }
        }
    }
    int flag=1;
    for(int i=1;i<=n+m;i++)
    {
        if(i==getf(i)&&!val[i])
        {
            cout<<"No\n";
            flag=0;
            break;
        }
    }
    if(flag)
    {
        cout<<"Yes\n";
    for(int i=1;i<=n;i++)
    {
        cout<<val[getf(i)]<<" ";
    }
    cout<<endl;
    for(int j=1;j<=m;j++)
    {
        cout<<val[getf(j+n)]<<" ";
    }
    cout<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值