1. 程式人生 > >Codeforces #345div1 C Table Compression (650C) 並查集

Codeforces #345div1 C Table Compression (650C) 並查集

題意:給你一個n*m的矩陣,需要在不改變每一行和每一列的大小關係的情況下壓縮一個矩陣,壓縮後的矩陣所有數的總和儘量的小。

思路:我們有這樣的初步設想:對於在一行或一列的數x,y,若x<y,則建立一條x的位置到y的位置的邊。之後進行拓撲排序的DP即可。然而會被卡邊數卡掉,所以需要其它的解法。

新思路:我們把所有的數排個序,這樣方便選對所有相同的數賦值。我們從小到大對所有的數賦值,合併這個數所在的行和列,選取相關的行和列中的最大值+1作為作為這個數的新值。

為什麼這樣做正確呢?可以類比拓撲排序的dp過程,我們所賦的新值不能破壞原來行和列的大小關係,所以要找相關的行和列中的最大值+1。用並查集可以快速判斷哪些行和列相關。

實現參考了fatice大神的程式碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
int ans[maxn],x[maxn],y[maxn],f[maxn],mx[maxn];
struct node{
	int x,y,val,pos;
	bool operator <(const node& rhs)const{
		return val<rhs.val;
	}
};
node a[maxn];
int n,m;
int num(int i,int j){
	return (i-1)*m+j;
}
inline int get(int x){
	if(x==f[x])return x;
	return f[x]=get(f[x]);
}
void merge(int x,int y){
	f[get(x)]=get(y);
} 
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			int pos=num(i,j);
			scanf("%d",&a[pos].val);
			a[pos].x=i,a[pos].y=j,a[pos].pos=pos;
		}
	sort(a+1,a+1+n*m);
	for(int i=1;i<=n+m;i++)
		f[i]=i;
	int i,j,k;
	for(i=1;i<=n*m;i=j){
		for(j=i;a[j].val==a[i].val&&j<=n*m;j++);
		for(k=i;k<j;k++)merge(a[k].x,a[k].y+n);
		for(k=i;k<j;k++){
			int tmp=get(a[k].x);
			mx[tmp]=max(mx[tmp],max(x[a[k].x],y[a[k].y]));
		}
		for(k=i;k<j;k++){
			ans[a[k].pos]=mx[get(a[k].x)]+1;
			x[a[k].x]=ans[a[k].pos];
			y[a[k].y]=ans[a[k].pos];
		}
		for(k=i;k<j;k++){
			mx[a[k].x]=0;
			f[a[k].x]=a[k].x;
			mx[a[k].y+n]=0;
			f[a[k].y+n]=a[k].y+n;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			printf("%d ",ans[num(i,j)]);
		}
		printf("\n");
	}
	return 0;	
}