codeforces 651E. Table Compression(强连通分量缩点,拓扑排序,dp)
E. Table Compression
time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Little Petya is now fond of data compression algorithms. He has already studied gz, bz, zip algorithms and many others. Inspired by the new knowledge, Petya is now developing the new compression algorithm which he wants to name dis.
Petya decided to compress tables. He is given a table a consisting of n rows and m columns that is filled with positive integers. He wants to build the table a' consisting of positive integers such that the relative order of the elements in each row and each column remains the same. That is, if in some row i of the initial table ai, j < ai, k, then in the resulting table a'i, j < a'i, k, and if ai, j = ai, k then a'i, j = a'i, k. Similarly, if in some column j of the initial table ai, j < ap, j then in compressed table a'i, j < a'p, j and if ai, j = ap, j then a'i, j = a'p, j.
Because large values require more space to store them, the maximum value in a' should be as small as possible.
Petya is good in theory, however, he needs your help to implement the algorithm.
Input
The first line of the input contains two integers n and m (, the number of rows and the number of columns of the table respectively.
Each of the following n rows contain m integers ai, j (1 ≤ ai, j ≤ 109) that are the values in the table.
Output
Output the compressed table in form of n lines each containing m integers.
If there exist several answers such that the maximum number in the compressed table is minimum possible, you are allowed to output any of them.
Examples
input
2 2
1 2
3 4
output
1 2
2 3
input
4 3
20 10 30
50 40 30
50 60 70
90 80 70
output
2 1 3
5 4 3
5 6 7
9 8 7
Note
In the first sample test, despite the fact a1, 2 ≠ a21, they are not located in the same row or column so they may become equal after the compression.
题意:给出一个 \(n*m\) 的矩阵,每个格子有一个数,现在要将矩阵"压缩",即使得每个格子上的数尽可能的小,但是在压缩后的矩阵中,每一行每一列的大小相对关系要与原矩阵相同,求压缩后的矩阵。
题解:当矩阵中每个数字都不相等时,那么对于每一行/每一列,将数字进行排序,设排序后的序列为 \(a[1],a[2],…,a[n]\),那么 \(a[i]\) 向 \(a[i+1]\) 连边,然后可以进行拓扑排序,对拓扑排序进行 \(dp\) 即可。
for(int i=1;i<=n;i++)
{
int u=topo[i];
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
d[v]=max(d[v],d[u]+1);
}
}
当行/列中有相同的数时,那么连边的时将相同的数之间互相连边,然后强连通分量缩点,再拓扑排序然后 \(dp\).
upd:看了杜教代码,感觉自己果然菜啊,其实可以将所有数字进行排序,然后扫一遍,记录每一行每一列前一个出现的值,然后对于同一行/列相等的情况用并查集缩点,其实也可以不用拓扑排序,从小到大扫一遍所有数字即可。
代码1:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<set>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1e6+1000;
struct node
{
int x,y,v;
bool operator < (const node& b)const{
return v<b.v;
}
}a[maxn],b[maxn];
int head[maxn],head1[maxn];
struct edge
{
int to,next;
}e[maxn*5],e1[maxn*5];
int tol=0;
int tol1=0;
void add(int u,int v)
{
e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
}
void add1(int u,int v)
{
e1[++tol1].to=v,e1[tol1].next=head1[u],head1[u]=tol1;
}
int n,m;
int get(int x,int y)
{
return (x-1)*m+y;
}
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
VI G[maxn];
void dfs(int u)
{
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!pre[v])
{
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
lowlink[u]=min(lowlink[u],pre[v]);
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
while(1)
{
int x=S.top(); S.pop();
sccno[x]=scc_cnt;
G[scc_cnt].pb(x);
if(x==u) break;
}
}
}
void find_scc(int n)
{
dfs_clock=scc_cnt=0;
for(int i=1;i<=n;i++)
if(!pre[i]) dfs(i);
}
set<int> st;
int c[maxn],topo[maxn];
int t;
bool dfs1(int u)
{
c[u]=-1;
for(int i=head1[u];i;i=e1[i].next)
{
int v=e1[i].to;
if(c[v]<0) return false;
else if(!c[v]&&!dfs1(v)) return false;
}
c[u]=1;topo[--t]=u;
return true;
}
bool toposort(int n)
{
t=n+1;
rep(i,1,n+1) if(!c[i]&&!dfs1(i)) return false;
return true;
}
int d[maxn];
int ans[maxn];
int main()
{
scanf("%d%d",&n,&m);
int tot=0;
rep(i,1,n+1) rep(j,1,m+1)
{
++tot;
a[tot].x=i,a[tot].y=j;
scanf("%d",&a[tot].v);
}
tot=0;
rep(j,1,m+1) rep(i,1,n+1)
{
++tot;
b[tot]=a[get(i,j)];
}
rep(j,1,m+1) sort(b+(j-1)*n+1,b+j*n+1);
rep(i,1,n+1) sort(a+(i-1)*m+1,a+i*m+1);
rep(i,1,n+1)
{
rep(j,1,m)
{
int u=get(i,j),v=get(i,j+1);
if(a[u].v<=a[v].v)
add(get(a[u].x,a[u].y),get(a[v].x,a[v].y));
}
per(j,2,m+1)
{
int u=get(i,j),v=get(i,j-1);
if(a[u].v<=a[v].v)
add(get(a[u].x,a[u].y),get(a[v].x,a[v].y));
}
}
rep(j,1,m+1)
{
rep(i,1,n)
{
int u=(j-1)*n+i,v=(j-1)*n+i+1;
if(b[u].v<=b[v].v)
add(get(b[u].x,b[u].y),get(b[v].x,b[v].y));
}
per(i,2,n+1)
{
int u=(j-1)*n+i,v=(j-1)*n+i-1;
if(b[u].v<=b[v].v)
add(get(b[u].x,b[u].y),get(b[v].x,b[v].y));
}
}
find_scc(n*m);
rep(i,1,scc_cnt+1)
{
st.clear();
for(int j=0;j<G[i].size();j++)
{
int u=G[i][j];
for(int k=head[u];k;k=e[k].next)
{
int v=e[k].to;
v=sccno[v];
if(v==i) continue;
if(st.find(v)==st.end())
{
add1(i,v);
st.insert(v);
}
}
}
}
toposort(scc_cnt);
rep(i,1,scc_cnt+1) d[i]=1;
rep(i,1,scc_cnt+1)
{
int u=topo[i];
for(int i=head1[u];i;i=e1[i].next)
{
int v=e1[i].to;
d[v]=max(d[v],d[u]+1);
}
}
rep(i,1,n+1) rep(j,1,m+1)
{
int t=(i-1)*m+j;
int tt=(a[t].x-1)*m+a[t].y;
ans[tt]=d[sccno[tt]];
}
rep(i,1,n+1)
{
rep(j,1,m+1)
{
int t=(i-1)*m+j;
printf("%d ",ans[t]);
}
puts("");
}
return 0;
}
代码2:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=100;
VI V[maxn];
int pa[maxn];
int find(int x)
{
return pa[x]==x? x:pa[x]=find(pa[x]);
}
pair<int,PII> p[maxn];
int mr[maxn],mc[maxn];
int val[maxn];
VI G[maxn];
int d[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
rep(i,0,n) rep(j,0,m)
{
scanf("%d",&p[i*m+j].fi);
p[i*m+j].se=make_pair(i,j);
val[i*m+j]=p[i*m+j].fi;
}
sort(p,p+n*m);
rep(i,0,n*m) pa[i]=i;
rep(i,0,n) mr[i]=-1;
rep(i,0,m) mc[i]=-1;
rep(i,0,n*m)
{
int r=p[i].se.fi,c=p[i].se.se;
if(mr[r]!=-1)
{
if(val[mr[r]]==p[i].fi) pa[r*m+c]=find(mr[r]);
else G[mr[r]].pb(r*m+c);
}
if(mc[c]!=-1)
{
if(val[mc[c]]==p[i].fi) pa[find(r*m+c)]=find(mc[c]); //注意此处r*m+c的父亲可能不是自己,因为可能在上一个if中有改变
else G[mc[c]].pb(r*m+c);
}
mr[r]=r*m+c,mc[c]=r*m+c;
}
rep(i,0,n*m) V[find(i)].pb(i),d[i]=1;
rep(i,0,n*m)
{
int r=p[i].se.fi,c=p[i].se.se;
int t=r*m+c;
if(find(t)==t)
{
for(int k=0;k<V[t].size();k++)
{
int u=V[t][k];
for(int j=0;j<G[u].size();j++)
{
int v=G[u][j];
d[find(v)]=max(d[find(v)],d[t]+1);
}
}
}
}
rep(i,0,n)
{
rep(j,0,m)
printf("%d ",d[find(i*m+j)]);
puts("");
}
return 0;
}