【2018/08/29】T2-拓扑排序-table (SDOJ 3728)

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/82183797

表格 

 给出一个表格,N 行 M 列,每个格子有一个整数,有些格子是空的。现在需要你来做出一些调整,使得每行都是非降序的。这个调整只能是整列的移动。 
 
【输入】 
第一行两个正整数 N 和 M。 
接下来 N 行,每行 M 个整数,-1 表示这个格子是空的,其他的整数都在 [0, 10^9]范围,表示格子的数字。 
【输出】 
若无解,输出 -1; 
否则输出任意一个解,即一行 M 个正整数 p1, p2, · · · , pm,表示可以把初始表格的 pi 列,放在新表格的第 i 列,以得到一个合法的表格。 
 
【样例输入1】 
3 3 
-1 -1 -1 
2 1 2 
2 -1 1 
 
【样例输出1】 
2 3 1 
【样例输入2】 
2 2 
1 2 
2 1 
【样例输出2】 
-1 


对于 20% 的数据,满足 1 ≤ N ≤ 8,1 ≤ M ≤ 8。 
对于 60% 的数据,满足 1 ≤ N × M ≤ 2 × 10^3。 
对于 100% 的数据,满足 1 ≤ N × M ≤ 10^5。 

分析

莫名其妙的爆搜居然卡爆了spj???我也很无奈 ldx大佬觉得这题正解很好想???我怎么一点也看不出来。。。哎,还是太弱了

由于最后排出来的每一行都是非降序的,意思就是说是升序的(不过相邻的可以相等)。那么就有一定的顺序了,某一列必须在某一列之前,诸如此类。我们就可以联想到拓扑排序,然后建个图,跑一遍Kahn,就出来答案了。只是要注意建图的时候,我们可以O(n)解决,先将这一行的数排一个序,然后前后两两连边,遇到相同的数的时候我们就建一个虚点,把每一个数都连向虚点,下一次再从虚点连出去,就保证了O(n)的复杂度(n^2 的做法就是每两个数连一条边)

无解的情况就是建完图后出现环,之前傻逼的在想如何快速判环(过路大佬麻烦指教一下),后面才发现直接最后看答案有没有m个即可

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<queue> 
#define in read()
#define ll long long
#define N 100009
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-48;ch=getchar();}
	return f==1?res:-res;
}
int n,m,nxt[4*N],head[N],to[4*N],du[N],tot=0;
int ans[N];
queue<int > q;
struct node{int pos,v;}a[N];
inline bool cmp(const node &a,const node &b){return a.v<b.v;}
inline void add(int x,int y){nxt[++tot]=head[x];head[x]=tot;to[tot]=y;}
int main(){
	n=in;m=in;
	int i,j,k,top=m;
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j)
		{ a[j].v=in;a[j].pos=j; }	
		sort(a+1,a+m+1,cmp);
		int l=1,r=1;
		k=-1;
		while(r<=m){
			while(r<=m&&a[l].v==a[r].v) r++;//第一个不等于a[l].v的位置 
			if(a[l].v==-1){
				l=r;continue;///////////////
			}	
			if(k!=-1)
				for(int p=l;p<r;++p) add(k,a[p].pos),++du[a[p].pos];//从上一个虚点连向当前点
			k=++top;
			for(int p=l;p<r;++p) add(a[p].pos,k),++du[k];//新建一个虚点并从当前点连出去
			l=r;//别忘了更新l
		}
	}
	int num=0;
	for(i=1;i<=top;++i)///////////////
		if(!du[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		if(u<=m){
			ans[++num]=u;
		}//只有当现在的这个点不是我们搞出来的虚点时才能记录进答案
		for(int e=head[u];e;e=nxt[e]){
			int v=to[e];
			--du[v];
			if(!du[v]) q.push(v);
		}
		
	}
	if(num!=m) printf("-1");
	else {
		for(i=1;i<=m;++i)
		   printf("%d ",ans[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/82183797