原题链接
题意:
有个厨师吃了2天,第一个天吃了n道菜,第二天吃了m道菜,然后给每道菜打分,给出一个n*m的字符矩阵,s[i][j]表示第一天的第i道菜和第二天的第j道菜的评分的大小关系,求这两天吃的菜的评分。
思路:
按>和=数量排序,先给最大的赋值1e4,每次取两个字符串对比,讨论一下各种组合情况,判掉不合法情况,求出第一天的评分,再根据每一列的符号求出第二天的评分。这时候求出来的只是相对大小,还要把所有的值在数轴上往左移,把最小的数移到1就可以了,细节比较多,很锻炼码力。
改到wa9的时候搜了一下题解,看到tag是拓扑排序+并查集,想了一会儿并没有思路,然后觉得模拟思路肯定没问题,只是有地方没考虑到,就继续干了(不得不说还是早上状态比较好,前一天晚上改到23点已经绝望了,第二天一来唰唰唰十分钟就想明白了)。
#include<bits/stdc++.h>
#define LL long long
#define INF INT64_MAX
#define MOD 998244353
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
typedef pair<int,int>pa;
const int N = 1e3+7;
struct node{
int pos, ma, dy;
}a[N];
int ans1[N], ans2[N];
string s[N];
bool cmp(node x, node y){
if(x.ma==y.ma) return x.dy>y.dy;
else return x.ma>y.ma;
}
int main(){
int n, m, k, fla = 0, f = 0;
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i++){
cin>>s[i];
a[i].pos = i;
for(int j = 0;j < m;j++){
if(s[i][j]=='>') a[i].ma++;
else if(s[i][j]=='=') a[i].dy++;
}
}
sort(a, a+n, cmp);
ans1[a[0].pos] = 1e4;
for(int i = 0;i < n-1;i++){
f = 0;
int oid = a[i].pos, id = a[i+1].pos;
//printf("oid = %d %d\n", oid, id);
if(s[oid]<s[id]){
fla = 1;
}
for(int j = 0;j < m;j++){
if(s[oid][j]=='>'){
if(s[id][j]=='>') continue;
if(s[id][j]=='='){ //比上一个小1的情况
if(f==1) fla = 1;
f = 2;
if(ans1[id]) ans1[id] = min(ans1[id], ans1[oid]-1); //这里要取最小值
else ans1[id] = ans1[oid]-1;
} else {
if(f==1) fla = 1;
f = 2;
ans1[id] = ans1[oid]-2; //比上一个小2的情况
}
}
else if(s[oid][j]=='='){
if(s[id][j]=='='){ //和上一个相等的情况
if(f==2) fla = 1;
f = 1;
ans1[id] = ans1[oid];
}
else if(s[id][j]=='>') fla = 1;
else{ //比上一个小1的情况
if(f==1) fla = 1;
f = 2;
if(ans1[id]) ans1[id] = min(ans1[id], ans1[oid]-1); //这里要取最小值
else ans1[id] = ans1[oid]-1;
}
}
else if(s[oid][j]=='<' && s[id][j]!='<') fla = 1;
}
if(!ans1[id]) ans1[id] = ans1[oid]; //所有符号都一样的时候等于上一个数
}
if(fla){printf("No\n");return 0;}
printf("Yes\n");
for(int j = 0;j < m;j++){
if(s[a[0].pos][j]=='<') {ans2[j] = ans1[a[0].pos]+1;continue;} //第一个符号就是<
if(s[a[n-1].pos][j]=='>'){ans2[j] = ans1[a[n-1].pos]-1; continue;} //最后一个符号还是>
if(ans1[a[0].pos]==ans1[a[n-1].pos]){ans2[j] = ans1[a[0].pos];continue;} //所有符号都是=
for(int i = 0;i < n-1;i++){
int id = a[i+1].pos, oid = a[i].pos;
if(s[oid][j]=='>'){
if(s[id][j]=='>') continue;
else if(s[id][j]=='=') ans2[j] = ans1[id]; //等于第二个数
else ans2[j] = ans1[id]+1; //在两个数中间
}
else if(s[oid][j]=='='){
if(s[id][j]=='=') ans2[j] = ans1[id]; //等于这两个数
else ans2[j] = ans1[id]+1; //等于第一个数(也等于第二个数+1)
}
break;
}
}
int Mi = 1e6;
for(int i = 0;i < m;i++) Mi = min(Mi, ans2[i]);
for(int i = 0;i < n;i++) Mi = min(Mi, ans1[i]);
for(int i = 0;i < n;i++) printf("%d ", ans1[i]-Mi+1);
printf("\n");
for(int i = 0;i < m;i++) printf("%d ", ans2[i]-Mi+1);
return 0;
}
补拓扑排序+并查集做法:
给n+m个点建有向边,大的指向小的,相等的用并查集相连,然后dfs跑到出度为0的点赋值1,再递归回去。
#include<bits/stdc++.h>
#define LL long long
#define INF INT64_MAX
#define MOD 998244353
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
typedef pair<int,int>pa;
const int N = 2e3+7;
int a[N], f[N], vis[N];
char s[N][N];
vector<int> v[N];
int Find(int x){
return x==f[x]? x : f[x]=Find(f[x]);
}
void Union(int x, int y){
int fx = Find(x);
int fy = Find(y);
if(fx!=fy) f[fx] = fy;
}
void dfs(int x){
vis[x] = 1;
for(auto it:v[x]){
if(vis[it]){
printf("No\n");
exit(0);
}
if(!a[it]) dfs(it);
a[x] = max(a[x], a[it]);
}
++a[x];
vis[x] = 0;
}
int main(){
int n, m, k;
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", s[i]+1);
for(int j = 1;j <= m;j++){
if(s[i][j]=='=') Union(i, j+n);
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(s[i][j]=='>') v[Find(i)].push_back(Find(j+n));
if(s[i][j]=='<') v[Find(j+n)].push_back(Find(i));
}
}
for(int i = 1;i <= n+m;i++){
if(!a[i]) dfs(i);
}
printf("Yes\n");
for(int i = 1;i <= n;i++) printf("%d ", a[Find(i)]);
printf("\n");
for(int i = 1;i <= m;i++) printf("%d ", a[Find(i+n)]);
return 0;
}