今天封神讲分治和倍增,分治还是比较神奇的一种思想的,能解决不少问题,思路就是把一个大问题划分成小问题,然后通过递归求解小问题,最终得到大问题的解。
- 归并排序
归并排序是分治的一个典型应用,具体实现就是把一个长序列不断二分,分成的两部分比较大小后再合并到一个新的数组,最终得到排序后的结果。
归并排序除了可以排序,还可以求逆序对,个人觉得比用树状数组要好想一些。
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