链接
题意:有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<bits/stdc++.h>
using namespace std;
char op[1005][1005];
int fa[2005];
int d[2005];
int findfa(int x){
return fa[x]=fa[x]==x?x:findfa(fa[x]);
}
int head[2005],cnt;
struct node{
int u,v,nxt;
}edge[2000005];
void addedge(int u,int v){
edge[++cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int ans[2005];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n+m;i++)fa[i]=i;
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;i<=n;i++)
scanf("%s",op[i]+1);
for(int i=1;i<=n;i++){//先缩点
for(int j=1;j<=m;j++){
if(op[i][j]=='='){
int fu=findfa(i),fv=findfa(n+j);
if(fu!=fv){
fa[fv]=fu;
}
}
}
}
for(int i=1;i<=n;i++){//大小关系建边
for(int j=1;j<=m;j++){
if(op[i][j]=='<'){
int fu=findfa(i),fv=findfa(n+j);
addedge(fu,fv);
d[fv]++;
}
else if(op[i][j]=='>'){
int fu=findfa(i),fv=findfa(n+j);
addedge(fv,fu);
d[fu]++;
}
}
}
int now=0;
queue<int>q;
int fas=0;
for(int i=1;i<=n+m;i++){//找一开始入度为0的点
if(fa[i]==i){
fas++;//统计一下有多少个参与拓扑的点
if(d[i]==0){
q.push(i);
ans[i]=1;
}
}
}
while(!q.empty()){
int u=q.front();
q.pop();
fas--;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
d[v]--;
if(d[v]==0&&fa[v]==v){
q.push(v);
ans[v]=ans[u]+1;
}
}
}
if(fas!=0){//如果应该参与拓扑的点没有全部被访问到,就说明有环
printf("No\n");
}
else{
printf("Yes\n");
for(int i=1;i<=n+m;i++){
int fu=findfa(i);
ans[i]=ans[fu];
}
for(int i=1;i<=n;i++){
printf("%d%c",ans[i],i==n?'\n':' ');
}
for(int i=n+1;i<=n+m;i++){
printf("%d%c",ans[i],i==n+m?'\n':' ');
}
}
return 0;
}