[CF1500C] Matrix Sorting (模拟)

场上最后十几秒交上去过掉了耶!

题面

这里有两个 N ∗ M N*M NM E x c e l \rm Excel Excel 表格 A A A B B B

我们知道 E x c e l \rm Excel Excel 表格有一种排序功能,就是把每一行按照某一列的元素排序,这一列元素相同的行保持原相对顺序。

问表格 A A A 怎样通过上述的排序变化成表格 B B B

如果这是不可能的,输出 -1

否则输出 K K K K K K 个整数 c 1 , c 2 , . . . , c K c_1,c_2,...,c_K c1,c2,...,cK 表示依次用来排序的列的编号。可重复排序某一列。

N , M ≤ 1500 N,M\leq 1500 N,M1500

题解

首先,做 E x c e l \rm Excel Excel 的经验告诉我们,每一列最多操作一次就够了。

然后,我们倒着想, A A A 只要不是一开始就等于 B B B,那么它一定会排个序,既然要排个序,那么 B B B 就一定有至少一列是有序的。

我们就假定这列有序的为最后操作的一列。如果有两列及以上同时有序,那么无论最后操作的是哪一列都一样。

于是这一列里相同的连续段就隐含着倒数第二个操作列的线索,因为它们的原相对顺序是不变的。(如果没有相同连续段那自然好,我们就把之前确定的操作列模拟一遍,再判断就行)我们不妨假定有 k k k 个连续段 q 1 , q 2 , . . . , q k q_1,q_2,...,q_k q1,q2,...,qk q i = [ l i , r i ] , l i < r i q_i=[l_i,r_i],l_i<r_i qi=[li,ri],li<ri)。

我们再看有哪些列满足:没有操作过,且分别 k k k 个连续段内都有序。找到这样的一列,如果有多个就任取一个,然后这列就成为了倒数第二操作列。当然,此时在原来 k k k 个连续段内可能还有这一操作列上的连续段。如果有,那么我们再次假定有 k ′ k' k 个连续段 q 1 ′ , q 2 ′ , . . . , q k ′ ′ q'_1,q'_2,...,q'_{k'} q1,q2,...,qk,容易发现, q 1 ′ ∪ q 2 ′ ∪ . . . ∪ q k ′ ′ q'_1\cup q'_2\cup...\cup q'_{k'} q1q2...qk 刚好是 倒数第一操作列的连续段并集倒数第二操作列的连续段并集 的交集 再除去长度为 1 的单独区间

类似的求出倒数第三操作列、倒数第四操作列……直到每一列都操作过或者没有连续段时停止。

最后把 A A A 按照求出的操作列序列模拟排个序,与 B B B 比较就是了。这里并不需要真的每次 O ( N M l o g N ) \rm O(NMlogN) O(NMlogN) 排个序,只需要对一个标号数组排序就行了。注意:<algorithm> 内置 sort 并不是稳定的排序

CODE

#include<set>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1505
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
int n,m,i,j,s,o,k;
int a[MAXN][MAXN],b[MAXN][MAXN];
int cc[5005],cnc;
struct it{
	int l,r;it(){l=r=0;}
	it(int L,int R){l=L;r=R;}
}q[MAXN],q2[MAXN],c[MAXN];
int cnt;
bool f[MAXN];
int NW;
bool cmp(it x,it y) {
	if(a[x.l][NW] != a[y.l][NW]) return a[x.l][NW] < a[y.l][NW];
	else return x.r < y.r;
}
int main() {
	n = read(); m = read();
	for(int i = 1;i <= n;i ++) {
		c[i] = it(i,i);
		for(int j = 1;j <= m;j ++) a[i][j] = read();
	}
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= m;j ++) b[i][j] = read();
	}
	q[cnt = 1] = it(1,n);
	while(cnt && cnc < 5000) {
		int cn = 0,ff = 0;
		for(int i = 1;i <= m;i ++) {
			if(f[i]) continue;
			bool flag = 1;
			for(int j = 1;j <= cnt;j ++) {
				for(int k = q[j].l+1;k <= q[j].r;k ++) {
					if(b[k][i] < b[k-1][i]) {flag = 0;break;}
				}
				if(!flag) break;
			}
			if(flag) {
				cc[++ cnc] = i;
				f[i] = 1; ff = 1;
				for(int j = 1;j <= cnt;j ++) {
					for(int k = q[j].l;k < q[j].r;k ++) {
						if(b[k+1][i] == b[k][i]) {
							int ll = k;k ++;
							while(k < q[j].r && b[k+1][i] == b[k][i]) k ++;
							q2[++ cn] = it(ll,k);
						}
					}
					if(!flag) break;
				}	
				break;
			}
		}
		for(int i = 1;i <= cn;i ++) q[i] = q2[i]; cnt = cn;
	}
	for(int i = cnc;i > 0;i --) {
		NW = cc[i];
		sort(c + 1,c + 1 + n,cmp);
		for(int j = 1;j <= n;j ++) c[j].r = j;
	}
	bool flag = 1;
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= m;j ++) {
			if(a[c[i].l][j] != b[i][j]) flag = 0;
		}
	}
	if(flag) {
		printf("%d\n",cnc);
		for(int i = cnc;i > 0;i --) printf("%d ",cc[i]);ENDL;
	}
	else printf("-1\n");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值