H - 郭大侠与英雄学院
就是给你一个n*m的矩阵,然后叫你缩小这个矩阵,而且每行每列的数之间的大小关系不变,而且全为正数。
开始时想了好久也没想出来
先讨论没有重复点的情况,就是先把所有点按值升序排序,然后依次往新矩阵里放,放时的新的值为max(它所在行的最大值,它所在列的最大值)+1。然后这么放就行
但是有重复点,就比较麻烦了,而且只有同行同列的重复点才会产生影响,就是他们在新矩阵的值必须是一样的,如果不同行不同列,那么就没有影响
然后对于同行同列的相等的数,就必须用并查集把他们放到同一个集合里,
然后就是每行每列的扫过去就行,然后我是用set查的,遇到重复就并查集合并,那并查集用什么来编号呢,我是用的他们的位置,把二维转一维后按位置编号,这样编号就唯一啦,然后后面操作也方便
然后也是按值升序sort,然后从小到大往里面放数
还是考虑重复的情况,对于同一行同一列,相同数必须也要有对应的相同的新值,但是我们是按值的大小放的,所以所有相同的值会连续的放完,然后再放下一批相同的数,这样的话,我们就需要保存每行每列上的最大值,和这个最大值对应的之前的值的所在集合的根节点的那个编号,为什么要保存这个呢,首先要保存最大值是肯定的,然后因为考虑到相同的值是同一个集合嘛,所以要保存最大值所在集合的编号
然后后面更新的时候,如果发现当前值所在集合的编号和目前所在行/列的最大值的编号一样,就说明已经出现过了,不要再处理了,然后如果不一样的话,就是max(当前值所在集合的对应值,行最大值+1),然后再max(当前值所在集合的对应值,列最大值+1)。当然,我把所有数的初始对应值都设为0。
然后因为我们所有同一集合里的数必须有相同的新值,所以我们更新时更新的是这个数的根节点的对应值,
这样操作完之后,就该输出啦,输出时我们也是输出这个点的根节点的新值就好啦
end
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define ll long long
#define maxn 1000005
int n, m;
struct Node
{
int num, idx;
void make(int n, int i)
{
num=n,idx=i;
}
bool operator <(Node x)const
{
return num < x.num;
}
bool operator ==(Node x)const
{
return num == x.num;
}
bool operator <=(Node x)const
{
return num <= x.num;
}
}grid[maxn], row[maxn], column[maxn];
int aim[maxn];
int parent[maxn];
int size[maxn];//结点数
void init_parent()
{
for (int i = 0; i<maxn; ++i)
{
parent[i] = i;
size[i] = 1;
}
}
int findparent(int x)//路径压缩
{
if (x == parent[x])
return x;
else
return parent[x] = findparent(parent[x]);
}
void unionparent(int x, int y)//启发式合并
{
int fx = findparent(x);
int fy = findparent(y);
if (fy == fx)
return;
if (size[fx] >= size[fy])
{
size[fx] += size[fy];
parent[fy] = fx;
}
else
{
size[fy] += size[fx];
parent[fx] = fy;
}
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d%d", &n, &m);
init_parent();
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
scanf("%d", &grid[i*m + j].num);
grid[i*m + j].idx = i*m + j;
}
set<Node> tmp;
set<Node>::iterator itor;
for (int i = 0; i < n; ++i)
{
tmp.clear();
for (int j = 0; j < m; ++j)
{
itor = tmp.find(grid[i*m + j]);
if (itor == tmp.end() || itor->num != grid[i*m + j].num)
tmp.insert(grid[i*m + j]);
else
unionparent(itor->idx, grid[i*m + j].idx);
}
}
for (int i = 0; i < m; ++i)
{
tmp.clear();
for (int j = 0; j < n; ++j)
{
itor = tmp.find(grid[j*m + i]);
if (itor == tmp.end() || itor->num != grid[j* m + i].num)
tmp.insert(grid[j*m + i]);
else
unionparent(itor->idx, grid[j*m + i].idx);
}
}
/*for (int i = 0; i < n*m; ++i)
printf("%d ", findparent(i));
printf("\n");*/
sort(grid, grid + n*m);
/*for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
printf("%d %d\n", grid[i*m + j].num, grid[i*m + j].idx);
}
printf("\n");*/
for (int i = 0; i < n*m; ++i)
row[i].idx = -1, column[i].idx = -1;
for (int i = 0; i < n*m; ++i)
{
int idx = grid[i].idx;
int pa = findparent(idx);
if (row[idx/m].idx!=pa)
aim[pa] = max(aim[pa], row[idx / m].num + 1);
if (column[idx%m].idx != pa)
aim[pa] = max(aim[pa], column[idx%m].num + 1);
//row[idx / m] = max(row[idx / m], aim[pa]);
row[idx / m].make(aim[pa], pa);
column[idx%m].make(aim[pa], pa);
//column[idx%m] = max(column[idx%m], aim[pa]);
//printf("%d %d %d %d\n", pa, aim[pa], row[idx / m], column[idx%m]);
}
//printf("ans:\n");
for (int i = 0; i < n; ++i)
{
//printf("%d", aim[findparent(i*n)]);
for (int j = 0; j < m; ++j)
printf("%d ", aim[findparent(i*m + j)]);
printf("\n");
}
//system("pause");
//while (1);
return 0;
}