利用分治策略解題

海倫凱樂發表於2016-07-03

今天封神講分治和倍增,分治還是比較神奇的一種思想的,能解決不少問題,思路就是把一個大問題劃分成小問題,然後通過遞迴求解小問題,最終得到大問題的解。

  • 歸併排序
    歸併排序是分治的一個典型應用,具體實現就是把一個長序列不斷二分,分成的兩部分比較大小後再合併到一個新的陣列,最終得到排序後的結果。
    歸併排序除了可以排序,還可以求逆序對,個人覺得比用樹狀陣列要好想一些。
    POJ2299
    裸的求逆序對,方法是每次合併前後兩個序列時,如果後面的陣列要先於前面的插入,則這個數和前面的每一個數都構成一個逆序對。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500000+10;
int n;
long long a[maxn],tmp[maxn],ans;
void merge(int l,int m,int r){
     int i=l,j=m+1,k=l;
     while(i<=m&&j<=r){
         if(a[i]>a[j]){
             tmp[k++]=a[j++];
             ans+=m-i+1;
         }
         else{
             tmp[k++]=a[i++]; 
         }                  
     }
     while(i<=m) tmp[k++]=a[i++];
     while(j<=r) tmp[k++]=a[j++];
     for(int p=l;p<=r;p++){
         a[p]=tmp[p];        
     }
}
void merge_sort(int l,int r){
     if(l<r){
         int mid=(l+r)/2;
         merge_sort(l,mid);
         merge_sort(mid+1,r);
         merge(l,mid,r);
     }
}
int main(){
    while(scanf("%d",&n)){
        if(n==0) break;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);        
        }
        ans=0;
        merge_sort(1,n);
        printf("%lld\n",ans);        
    } 
    return 0;
}
  • 平面上的最近點對
    這個貌似是個很洋氣的題啊,資料範圍很大,考慮分治策略。
    將平面的點按x座標排序,然後將區間(l,r)分為(l,mid)和(mid+1,r),分別遞迴求解左右兩個區間的最短距離,還有一種情況,兩個點橫跨兩個區間,這樣的點需要特殊處理。
    處理也挺麻煩的,詳見程式碼。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <iomanip>
#include<cmath>
using namespace std;
const int maxn=200000+10;
const double INF=1e100;
int n;
double ans;
struct node{
    long long x,y;
    int set;       
}p[maxn];
int t[maxn];

int cmp(node a,node b){
    if(a.x!=b.x) return a.x<b.x;   
    else return a.y<b.y;
}

int cmp2(int a,int b){
    return p[a].y<p[b].y;   
}

double dist(int x,int y){
    if(p[x].set==p[y].set) return INF;   
    double ans2= sqrt((p[x].x-p[y].x)*(p[x].x-p[y].x) + (p[x].y-p[y].y)*(p[x].y-p[y].y));
    return ans2;
}

double work(int l,int r){
    if(l==r) return INF;
    if(l==r-1) return dist(l,r);
    int m=(l+r)/2;
    double minl=work(l,m);
    double minr=work(m+1,r);
    double minlr=min(minl,minr);

    int k=0;
    for(int i=l;i<=r;i++){
        if(p[i].x-p[i+1].x<=minlr){
            k++;
            t[k]=i;                    
        }        
    }

    sort(t+1,t+k+1,cmp2);

    for(int i=1;i<=k;i++){
        for(int j=i+1;j<=k;j++){
                if(p[t[i]].y-p[t[j]].y >=minlr|| p[t[i]].set==p[t[j]].set) break;
            minlr=min(minlr,dist(t[i],t[j]));    
        }       
    }
    return minlr;
}

int main(){
    int T;
    scanf("%d",&T);
    for(int c=1;c<=T;c++){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
             scanf("%lld%lld",&p[i].x,&p[i].y); 
             p[i].set=1;      
        }
        for(int i=n+1;i<=2*n;i++){
             scanf("%lld%lld",&p[i].x,&p[i].y);   
             p[i].set=2;      
        }  
        n*=2;
        sort(p+1,p+n+1,cmp);
        ans=work(1,n);
        //printf("%.3lf\n",ans);
        cout<<setiosflags(ios::fixed)<<setprecision(3)<<ans<<endl;      
    }
    return 0;
}
                                                helenkeller
                                                 2016.7.3

相關文章