zoukankan      html  css  js  c++  java
  • 【CF1500C】Matrix Sorting

    题目

    题目链接:https://codeforces.com/problemset/problem/1500/C
    给你两个 \(n\times m\) 的矩阵 \(A,B\)\(1\le n,m\le 1500\)),矩阵的元素均为 \([1,n]\) 内的整数。
    每一次操作你可以选定一列作为每一行的关键字,按照关键字从小到大的顺序把所有行排序得到一个新矩阵。这里使用的排序是稳定的,即如果有两行的关键字相同,则按照在原矩阵的先后顺序排序。
    你可以进行不超过 \(5000\) 次操作,问你能否将 \(A\) 变成 \(B\)。不能变成输出 \(-1\),否则输出一种可行的操作序列。

    思路

    首先可以把两个矩阵的每一行一一对应。如果有相同的行肯定是按照顺序对应,因为无论怎么排序他们之间相对顺序不会变。
    \(B\) 矩阵第 \(i\) 行对应 \(A\) 矩阵第 \(id_i\) 行。
    那么我们只需要让所有 \(id_i\) 行最后相对顺序小于 \(id_{i+1}\) 行。
    对于 \(id_i\)\(id_{i+1}\) 这两行,一个操作 \(j\),可能会让他们之间的相对顺序变为正确的,变为错误的,或者不改变。我们只需要满足最后一次改变他们之间相对顺序的操作,是将他们相对顺序改为正确的即可。
    显然一列最多拿来排序一次。考虑倒着处理。对于按照第 \(j\) 列排序这个操作,如果他会把 \(id_i\)\(id_{i+1}\) 的相对顺序改为正确的,那么就从 \(j+n\)\(i\) 连有向边;如果会改为错误的,就从 \(i\)\(j+n\) 连有向边。
    然后进行拓扑排序。注意这里对于 \(i\leq n\) 的点,只要被访问到一次就可以进队列了。
    最后如果访问到了 \(n-1\)\(\leq n\) 的点,说明有解,把拓扑序中 \(>n\) 的操作编号逆序输出即可。
    注意可能到最后都存在 \(id_i\)\(id_{i+1}\) 没有被任何操作改变相对顺序的情况,这时候就要判断他们初始顺序是否正确。
    时间复杂度 \(O(nm)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    
    const int N=3010;
    const ull b1=131,p1=849322843;
    const ull b2=13331,p2=1450295533;
    const ull b3=1145141,p3=1839220993;
    int n,m,cnt,tot,deg[N],head[N],a[N][N],id[N];
    ull h1[2][N],h2[2][N],h3[2][N];
    bool vis[N];
    queue<int> q;
    stack<int> st;
    
    struct edge
    {
    	int next,to;
    }e[N*N];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot; deg[to]++;
    }
    
    void topsort()
    {
    	for (int i=n+1;i<=n+m;i++)
    		if (!deg[i]) q.push(i);
    	while (q.size())
    	{
    		int u=q.front(); q.pop();
    		if (u<=n) cnt++;
    			else st.push(u-n);
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (v<=n && !vis[v])
    				vis[v]=1,q.push(v);
    			if (v>n)
    			{
    				deg[v]--;
    				if (!deg[v]) q.push(v);
    			}
    		}
    	}
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    		{
    			scanf("%d",&a[i][j]);
    			h1[0][i]=(h1[0][i]*b1+a[i][j])%p1;
    			h2[0][i]=(h2[0][i]*b2+a[i][j])%p2;
    			h3[0][i]=(h3[0][i]*b3+a[i][j])%p3;
    		}
    	for (int i=1;i<=n;i++)
    		for (int j=1,x;j<=m;j++)
    			{
    				scanf("%d",&x);
    				h1[1][i]=(h1[1][i]*b1+x)%p1;
    				h2[1][i]=(h2[1][i]*b2+x)%p2;
    				h3[1][i]=(h3[1][i]*b3+x)%p3;
    			}
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    			if (h1[0][i]==h1[1][j] && h2[0][i]==h2[1][j] && h3[0][i]==h3[1][j])
    			{
    				id[j]=i; h1[1][j]=1145141919810LL;
    				break; 
    			}
    	for (int i=1;i<n;i++)
    		for (int j=1;j<=m;j++)
    		{
    			if (a[id[i]][j]<a[id[i+1]][j]) add(j+n,i);
    			if (a[id[i]][j]>a[id[i+1]][j]) add(i,j+n);
    		}
    	topsort();
    	for (int i=1;i<n;i++)
    		if (!vis[i] && id[i]<id[i+1]) cnt++;
    	if (cnt<n-1) return printf("-1"),0;
    	printf("%d\n",(int)st.size());
    	for (;st.size();st.pop())
    		printf("%d ",st.top());
    	return 0;
    }
    
  • 相关阅读:
    pixysoft.framework.messageflow enterprise edition 开发实录
    软件工程革命三部曲 — 系统开发分类与重用说明
    vs2010 premium版本 使用小结 更多是问题。。
    报表引擎终于做出来了!!!
    报表引擎终于做出来了!!!!!参考了根兄的文档。
    页面驱动开发(Page Driven) —— 一种大多数人还不认同的技术
    从SOA到云计算 我个人理解
    谈谈Exception,什么时候抛出?什么时候接住? 二
    软件工程革命三部曲 —— 系统开发的业务部分重构在思考。
    Pixysoft.Framework.Reports 开发实录
  • 原文地址:https://www.cnblogs.com/stoorz/p/15543484.html
Copyright © 2011-2022 走看看