【cdoj 1338】郭大侠与英雄学院 并查集和矩阵那点事

ok,这道题我就写详细一点吧,毕竟剩下那几道题我都不想再写了,而且再好好消化一下子。

首先思考矩阵中没有重复元素的情况,这样的话就很简单了,只用把所有的元素输入进去再然后排序从小到大直接输出,在每一次输出的时候判断所在元素的这一行和列的最大编号(新的编号),然后将行和列中最大编号加一得到答案,有点类似于贪心的写法。然而,然而,让人郁闷的是会出现重复,那如果直接开一个vis数组储存当前点的最大不就行了吗?当然没有那么简单,因为在本题中,只有同行或者同列才会有影响,而如果不是同行或者同列,即使再相同,也不干人家什么屁事对吧。

既然需要把同行同列的元素一起考虑(他们需要一个相同的新编号),所以很容易想到把行和列相同的元素看成一个集合,而题目中会出现很多不同的集合,自然而然的想到并查集(然而我并没有想到怎么实现)。把同行同列相同的元素打包他们拥有相同的编号


再来说说实现吧,毕竟想对写不对是一件很恶心的是

先把所有元素从小到大排序,保证每一次把相同元素处理完,然后分段处理(每一段元素相同)

再用4次循环

第一次 :从相同元素的头到尾,找出在哪些行和列中元素出现过,并用数组把行和列出现过的元素记录(下一步有用)

2:因为是一个集合,把同行同列的打包加进来

3:找到一个集合的最小应该是多少,用根节点记录下来(因为是并查集) x=max

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;

struct node{
	int x,y,v;
	bool operator <(const node& b)const{
		return v<b.v;
	}
}cur[1000020];
int xma[1000020],yma[1000020],hx[1000020],hy[1000020],fa[1000020],ans[1000020];

int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}

void join(int x,int y){
	int dx=find(x);
	int dy=find(y);
	if(dx!=dy)fa[dx]=dy;
}

int main(){
	scanf("%d%d",&n,&m);
	int x,nu;
	int last;
	for(int i=1;i<=n*m;i++)fa[i]=i;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			nu=(i-1)*m+j;
			scanf("%d",&x);
			cur[nu].x=i;cur[nu].y=j;
			cur[nu].v=x;
		}
	}
	sort(cur+1,cur+n*m+1);
	last=1;
	for(int i=1;i<=m*n;i++){
		if(cur[i].v==cur[i+1].v&&i!=n*m)continue;
		for(int j=last;j<=i;j++){
			hx[cur[j].x]=(cur[j].x-1)*m+cur[j].y;
			hy[cur[j].y]=(cur[j].x-1)*m+cur[j].y;
		}
		for(int j=last;j<=i;j++){
			join((cur[j].x-1)*m+cur[j].y,hx[cur[j].x]);
			join((cur[j].x-1)*m+cur[j].y,hy[cur[j].y]);
		}
		for(int j=last;j<=i;j++){
			int x=cur[j].x,y=cur[j].y;
			int nu=(x-1)*m+y;
			int rt=find(nu);
			ans[rt]=max(ans[rt],max(xma[x],yma[y])+1);
		}
		for(int j=last;j<=i;j++){
			int x=cur[j].x,y=cur[j].y;
			int nu=(x-1)*m+y;
			xma[x]=max(xma[x],ans[find(nu)]);
			yma[y]=max(yma[y],ans[find(nu)]);
		}
		last=i+1;
	}
	for(int i=1;i<=m*n;i++){
		if(i%m==0){
			printf("%d \n",ans[find(i)]);
		}
		else printf("%d ",ans[find(i)]);
	}
	return 0;
}

( x , max ( hx[j] , hy[j] ) )

4.更新每行每列的最大值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值