poj 3241 Object Clustering (曼哈頓最小生成樹)
Time Limit: 2000MS | Memory Limit: 131072K | |
Total Submissions: 2640 | Accepted: 806 |
Description
We have N (N ≤ 10000) objects, and wish to classify them into several groups by judgement of their resemblance. To simply the model, each object has 2 indexes a and b (a, b ≤ 500). The resemblance of object i
Input
The first line contains two integers N and K. The following N lines each contain two integers a and b, which describe a object.
Output
A single line contains the minimum X.
Sample Input
6 2 1 2 2 3 2 2 3 4 4 3 3 1
Sample Output
2
題目大意:
(在平面坐標第一象限)給你n個整點,A(x1,y1)和B(x2,y2)兩點之間的距離定義為|x1-x2|+|y1-y2|,求最小生成樹第k大邊。
曼哈頓最小生成樹經典題。
首先這道題條件給的不充分啊:點都在第一象限(不包括坐標軸),沒有重(chong)點。
大致理一下思路:
首先,如果將點兩兩相連,邊數是O(n^2)的。但並非所有的邊都有用。網上有很多相關的證明,可以證得,只需對一二象限均分的四個部分分別求最近的點加邊。
其次,怎麽處理呢。先考慮y軸左偏45度的那一塊。先對y-x離散化,再對x排序(排序函數要註意,因為要把邊界算上),這樣每次查詢y-x和x比當前點大的點中最小值就好了(用線段樹)。這一塊實現起來確實比較麻煩,詳見代碼。
再次,其余三個部分通過坐標變換就可以套用第一部分的處理方式了。(x,y)(y,x)(-y,x)(x,-y)。
最後,跑個kruskal就ok了。
#include<cstdio> #include<algorithm> using namespace std; const int maxn=10000; const int inf=10000; struct tdot { int x,y; int num;//每個點的序號 int dis;//離散化後的值 }; tdot dot[maxn+10]; //x int cmp1(tdot a,tdot b) { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } //y-x int cmp2(tdot a,tdot b) { if(a.y-a.x==b.y-b.x) return a.x<b.x; return a.y-a.x<b.y-b.x; } //離散化及預處理 void discrete(int n) { sort(dot+1,dot+n+1,cmp2); for(int i=1;i<=n;i++) dot[i].dis=i; sort(dot+1,dot+n+1,cmp1); } struct ttree { int l,r; int num; int mmin; }; ttree tree[maxn*4+10]; void pushup(int x) { if(tree[x].l==tree[x].r) return; if(tree[x*2].mmin<=tree[x*2+1].mmin) tree[x].mmin=tree[x*2].mmin,tree[x].num=tree[x*2].num; else tree[x].mmin=tree[x*2+1].mmin,tree[x].num=tree[x*2+1].num; } void build(int x,int l,int r) { tree[x].l=l;tree[x].r=r; tree[x].mmin=inf; tree[x].num=0; if(l<r) { int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); } } void modify(int x,int pos,int mmin,int num) { if(tree[x].l==tree[x].r) tree[x].mmin=mmin,tree[x].num=num; else { int mid=(tree[x].l+tree[x].r)/2; if(pos<=mid) modify(x*2,pos,mmin,num); else modify(x*2+1,pos,mmin,num); pushup(x); } } struct tret { int mmin,num; }; tret query(int x,int l,int r) { tret ret; ret.mmin=inf;ret.num=0; if(l<=tree[x].l&&r>=tree[x].r) { ret.mmin=tree[x].mmin; ret.num=tree[x].num; return ret; } int mid=(tree[x].l+tree[x].r)/2; if(l<=mid) { tret ret0=query(x*2,l,r); if(ret.mmin>ret0.mmin) ret.mmin=ret0.mmin,ret.num=ret0.num; } if(r>mid) { tret ret0=query(x*2+1,l,r); if(ret.mmin>ret0.mmin) ret.mmin=ret0.mmin,ret.num=ret0.num; } return ret; } struct tedge { int a,b; int w; }; tedge edge[maxn*4+10]; int cnt=1; //加邊 void addedge(int n) { build(1,1,n); for(int i=n;i>=1;i--) { tret ret=query(1,dot[i].dis,n); if(ret.num!=0) { edge[cnt].a=dot[i].num; edge[cnt].b=ret.num; edge[cnt++].w=ret.mmin-dot[i].x-dot[i].y; } modify(1,dot[i].dis,dot[i].x+dot[i].y,dot[i].num); } } //kruskal用比較函數 int cmp(tedge a,tedge b) { return a.w<b.w; } int fa[maxn+10]; int father(int x) { if(fa[x]==x) return x; return fa[x]=father(fa[x]); } int main() { int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d%d",&dot[i].x,&dot[i].y); dot[i].num=i; } discrete(n); addedge(n); for(int i=1;i<=n;i++) swap(dot[i].x,dot[i].y); discrete(n); addedge(n); for(int i=1;i<=n;i++) dot[i].x*=-1; discrete(n); addedge(n); for(int i=1;i<=n;i++) swap(dot[i].x,dot[i].y); discrete(n); addedge(n); //kruskal for(int i=1;i<=n;i++) fa[i]=i; sort(edge+1,edge+cnt,cmp); for(int i=1,j=0;i<cnt;i++) { int fx=father(edge[i].a); int fy=father(edge[i].b); if(fx!=fy) { fa[fx]=fy; j++; } if(j==n-k) { printf("%d\n",edge[i].w); break; } } return 0; }View Code
PS:看別人的解題報告只是一種學習的途徑。代碼的實現還是要靠自己寫。這兩者的深度是大不相同的,有些東西只有在自己寫代碼的時候才能體會到。這道題前前後後寫了三遍,終於過了,開心。
poj 3241 Object Clustering (曼哈頓最小生成樹)