题意:有个人吃了两天饭,第一天吃了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;
}