Problem
Solution
题目让你求的是,把原来的列数重新排位置,能够得到非递减字典序的序列。输出的是该位置安排的是原来的第几列,并且使你输出的内容是所有符合输出里面的最小字典序。
如样例一:
4 3
4 3 3
1 5 1
1 5 1
3 5 2
答案为
2 1 3
重新排位置后得到的序列为:
3 4 3
5 1 1
5 1 1
5 3 2
每一行都比上一行的字典序要大,符合该题的两种排序为2 1 3和2 3 1,选择字典序小的2 1 3输出。
解决方案为:从0~(m-1)列寻找没有递减对的列数,找到后从0~(n-1)行进行遍历,如果在当前的列数时,该行比上一行大,那么后面的所有递减对都不造成影响,把后面的递减对记录删除,不断寻找没有递减对的列数。
如已排好的序列:
3 7 1
5 4 9
5 6 2
6 2 7
6 4 2
尽管在第二列有7 4和6 2两对递减对,但是因为先前已经选出3 5和5 6,后续递减对不造成影响,和数字大小627>562是同一个道理。
Code
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 2000+5;
int n,m,arr[maxn][maxn],desc_num[maxn],ans[maxn];
bool vis[maxn],desc_allow[maxn];
int main() {
while(cin >> n >> m) {
memset(desc_num,0,sizeof(desc_num));
memset(desc_allow,0,sizeof(desc_allow));
memset(vis,0,sizeof(vis));
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
cin >> arr[i][j];
if(i && arr[i][j] < arr[i-1][j]) {
desc_num[j]++; //记录第j列有n个递减对
}
}
}
bool flag = false;
for(int i=0; i<m; i++) {
int res = 0;
for(int j=0; j<m; j++) {
res = j;
if(vis[j]) {
res++;
continue;
}
if(!desc_num[j]) {
break;
}
if(j == m-1) {
res++;
}
}
if(res == m) {
flag = true;
break;
}
ans[i] = res;
vis[res] = true;
for(int j=1; j<n; j++) {
if(desc_allow[j] || arr[j][res] <= arr[j-1][res]) {
continue;
} else {
desc_allow[j] = true; //记录第j和j-1行的后续的列里面允许有递减对
for(int k=0; k<m; k++) {
if(vis[k]) {
continue;
}
if(arr[j][k] < arr[j-1][k]) {
desc_num[k]--; //第j行和第j-1行的后续递减对不影响字典序大小
}
}
}
}
}
if(flag) {
puts("-1");
} else {
for(int i=0; i<m; i++) {
if(i != m-1) {
cout << ans[i]+1 << " ";
} else {
cout << ans[i]+1 << endl;
}
}
}
}
}