【NOIP模拟】表格

博客主要解析了图论构图方法,即对每行排序,数值小的列向数值大的列连有向边,相同数字建虚点并连边,之后进行拓扑排序,若有环则输出 -1,还给出了相关代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解析:

       我是真没往图论想。。。

       构图方法就是对于每一个行,排一个序,将数值小的向数值大的列连有向边,对于相同的数字建一个虚点,然后将这些数字给它连边就行了。然后拓扑排序,有环输出-1。

 

代码:

#include <bits/stdc++.h>
using namespace std;

const int Max=1000010;
const int inf=1e9;
int n,m,tot,size,head,tail,t,pos,k;
int first[Max],to[Max],ans[Max],p[Max],vis[Max];
struct shu{int to,next;};
shu edge[Max<<1];
struct jz{int id,num;};
jz a[Max];

inline int get_int()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') {f=-1;c=getchar();}
	for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+c-'0';
	return x*f;
}

inline bool comp(const jz &a,const jz &b){return a.num<b.num;}

inline void build(int x,int y)
{
	edge[++size].next=first[x];
	first[x]=size;
	edge[size].to=y;
	to[y]++;
}

inline bool topsort()
{
	queue<int>q;
	for(int i=m+1;i<=t;i++)if(!to[i] && vis[i]) q.push(i);
	int sum=0;head=tail=0;
	while(sum<k)
	{
	  head=0;
	  if(!q.size()) return 0;
	  while(q.size())
	  {
		int i=q.front();q.pop();
	  	to[i]=inf,p[++head]=i,sum++;
	  	if(i<=m) ans[++tail]=i;
	  }
	  for(register int i=1;i<=head;i++)
	    for(register int u=first[p[i]];u;u=edge[u].next)
		{
		  to[edge[u].to]--;
		  if(!to[edge[u].to]) q.push(edge[u].to);
	  	}
	}
	for(int i=1;i<=m;i++) if(!vis[i]) ans[++tail]=i;
	return 1;
}

inline void print(int x)
{
	if(x>9) print(x/10);
	putchar('0'+x%10);
}

int main()
{
	n=get_int(),m=get_int(),t=m+1;
	for(int i=1;i<=n;i++)
	{
	  pos=1,t++;
	  for(int j=1;j<=m;j++) a[j].num=get_int(),a[j].id=j;
	  sort(a+1,a+m+1,comp);
	  while(a[pos].num==-1) pos++;
	  head=tail=pos;
	  while(pos<=m)
	  {
	 	while(a[pos+1].num==a[pos].num && pos<=m) pos++,tail++;
	 	for(int j=head;j<=tail;j++) build(t-1,a[j].id),build(a[j].id,t),vis[a[j].id]=1;
	 	vis[t-1]=1,vis[t]=1;head=tail=++pos,t++;
	  }
	}
	for(int i=1;i<=t;i++) if(vis[i]) k++;
	if(topsort()) for(int i=1;i<=m;i++) print(ans[i]),putchar(' ');
	else cout<<"-1";
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值