POJ 3241 Object Clustering 平面曼哈顿最小生成树

来源:互联网 发布:linux修改系统时间命令 编辑:程序博客网 时间:2024/06/06 09:58

题意:给出N个点的平面坐标,把它们划分为K个集合,在每个点集内建边,令点集连通,其中边权为曼哈顿距离,问所有边的最大边最小可能是多少。

范围:N<=10000 ,点集坐标范围 [1,500]

解法:如果K为1,即整个图的最小生成树中最大边。(这里利用了最小生成树的性质,即在所有联通图中,MST中的最大边是最小的,可以比较容易的反证得出)

  那么K不为1,即可以从最小生成树种删除(K-1)条最大边,剩下边中的最大边即是答案。

  曼哈顿最小生成树的建图有专门的方法可以让 N*(N-1)条边降低到 N条,然后直接求即可。

代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#include<iostream>#include<stdlib.h>#include<set>#include<map>#include<queue>#include<vector>#include<bitset>#pragma comment(linker, "/STACK:1024000000,1024000000")template <class T>bool scanff(T &ret){ //Faster Input    char c; int sgn; T bit=0.1;    if(c=getchar(),c==EOF) return 0;    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();    sgn=(c=='-')?-1:1;    ret=(c=='-')?0:(c-'0');    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');    if(c==' '||c=='\n'){ ret*=sgn; return 1; }    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;    ret*=sgn;    return 1;}#define inf 1073741823#define llinf 4611686018427387903LL#define PI acos(-1.0)#define lth (th<<1)#define rth (th<<1|1)#define rep(i,a,b) for(int i=a;i<=b;i++)#define drep(i,a,b) for(int i=a;i>=b;i--)#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)#define mem(x,val) memset(x,val,sizeof(x))#define mkp(a,b) make_pair(a,b)#define findx(x) lower_bound(b+1,b+1+bn,x)-b#define pb(x) push_back(x)using namespace std;typedef long long ll;typedef pair<int,int> pii;const int NN =100100;int n,m,k,b[NN],bn,dn;//b[]用于y-x的离散化struct point{    int x,y,idx;    bool operator < (const point temp)const{//x从小往大排,相同时y从小至大        if(x!=temp.x)return x<temp.x;        else return y<temp.y;    }}a[NN],c[NN];//a[]记录的是原数据,c[]是旋转后的坐标struct node{    int val,idx; //val为最小的x+y,idx为最小值所在的a[]的下标}t[1080000];void built(){    for(m=1;m<bn+2;m<<=1);    rep(i,1,m*2)t[i].val=inf;}void add(int x,int y,int idx){ //线段树维护新增点    int pos=findx(y-x);    int val=x+y;    for(int i=pos+m;i>0;i>>=1){        if(val<t[i].val){            t[i].val=val;            t[i].idx=idx;        }    }}int query(int x,int y){ //返回符合要求的最小的x+y的idx    int l=findx(y-x);    int r=m-1;    int ans=inf,idx=0;    for(l=l+m-1,r=r+m+1;l^r^1;l>>=1,r>>=1){        if(~l&1)if(t[l^1].val<ans)ans=t[l^1].val,idx=t[l^1].idx;        if(r&1) if(t[r^1].val<ans)ans=t[r^1].val,idx=t[r^1].idx;    }    return idx;}int f[NN]; //并查集用于求解MSTint find(int x){    return f[x]==x?x:find(f[x]=f[f[x]]);}int connec(int x,int y){    int i=find(x);    int j=find(y);    if(i==j)return 0;    f[i]=j;    return 1;}struct line{    int x,y,dis;    void caldis(){        dis=abs(a[x].x-a[y].x)+abs(a[x].y-a[y].y);    }}d[NN*8];bool cmpd(line x,line y){    return x.dis<y.dis;}ll solve(){    dn=0;    rep(i,1,n)c[i]=a[i]; //做一下原数组的copy,因为坐标转换后会改变,需用原数组求距离    rep(fx,1,4){        rep(i,1,n){//进行对称操作,让现在所求的R1实际上是R2-R4            if(fx==2||fx==4)swap(c[i].x,c[i].y);            if(fx==3)c[i].x=-c[i].x;        }        //离散化y-x        bn=0;        rep(i,1,n)b[++bn]=c[i].y-c[i].x;        sort(b+1,b+1+bn);        bn=unique(b+1,b+1+bn)-b-1;        //对点集排序        sort(c+1,c+1+n);        built();        drep(i,n,1){//X自大到小遍历点,保证线段树内的横坐标满足条件            int x=c[i].idx;            int y=query(c[i].x,c[i].y);//询问线段树得到idx            if(y!=0){                dn++;                d[dn].x=x;                d[dn].y=y;                d[dn].caldis();            }            add(c[i].x,c[i].y,c[i].idx);//添加此点进线段树            //此语句放query之后,很重要!        }    }    //求解MST    sort(d+1,d+1+dn,cmpd);    rep(i,1,n)f[i]=i;    int cot=0;    rep(i,1,dn){        if(connec(d[i].x,d[i].y)){            cot++;            if(cot==n-k)return d[i].dis;        }    }    return 0;}int main(){    while(scanf("%d%d",&n,&k)!=EOF){        rep(i,1,n)scanff(a[i].x),scanff(a[i].y),a[i].idx=i;        printf("%d\n",solve());    }    return 0;}


0 0
原创粉丝点击