POJ 3714 分治/求平面最近点对

第一次见这种问题直接懵圈。。。没想到分治法这么强大,借鉴了lyd的代码:

代码如下

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
const int maxn=200010;
struct point{
    int x,y,z;
};
point a[maxn],b[maxn];
int n,m,t,i;
double ans=0;
bool cmp(point a1,point a2){
    return a1.x<a2.x;
}
double dist(point a1,point a2){
    return sqrt(1.0*(a1.x-a2.x)*(a1.x-a2.x)+1.0*(a1.y-a2.y)*(a1.y-a2.y));
}
void merge(int l,int mid,int r){//按照y坐标排序,分治法 
    int i=l,j=mid+1,k;
    for(k=l;k<=r;k++){//两个部分已经按y坐标排好序,直接合并 
        if(j>r||i<=mid&&a[i].y<a[j].y)b[k]=a[i++];
        else b[k]=a[j++];
    }
    for(k=l;k<=r;k++)a[k]=b[k];
}
void solve(int l,int r){
    if(l==r)return;//分治边界
    int mid=(l+r)>>1,p=0,x=a[mid].x,i,j;
    solve(l,mid);//解决左半部分 
    solve(mid+1,r);//右半部分 
    merge(l,mid,r);//合并 
    for(i=l;i<=r;i++){//排除不肯能选项 
        if(abs(a[i].x-x)<=ans)b[++p]=a[i];
    }
    for(i=1;i<p;i++)
        for(j=i+1;j<=p;j++){
            if(b[j].y-b[i].y<ans){//y坐标之差小于ans才有可能是 
                if(b[i].z!=b[j].z)//如果不在一个集合 
                    ans=min(ans,dist(b[i],b[j]));
            }
            else//若这个不行,后面的肯定也不行 
                break;
        }
}
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),a[i].z=1;
        for(i=n+1,n*=2;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),a[i].z=2;
        ans=dist(a[1],a[n]);
        sort(a+1,a+1+n,cmp);//根据x坐标排序 
        solve(1,n);
        printf("%.3f\n",ans);
    }
}

 

转载于:https://www.cnblogs.com/pkgunboat/p/9315212.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是 poj3714 突袭的代码实现,使用了 Kruskal 算法解最小生成树: ```c++ #include <iostream> #include <algorithm> #include <vector> using namespace std; const int MAXN = 20005; const int MAXM = 100005; struct Edge { int u, v, w; bool operator<(const Edge& other) const { return w < other.w; } }; int n, m, p; int parent[MAXN], depth[MAXN]; Edge edges[MAXM]; void make_set(int v) { parent[v] = v; depth[v] = 0; } int find_set(int v) { if (v == parent[v]) { return v; } return parent[v] = find_set(parent[v]); } void union_sets(int a, int b) { a = find_set(a); b = find_set(b); if (a != b) { if (depth[a] < depth[b]) { swap(a, b); } parent[b] = a; if (depth[a] == depth[b]) { depth[a]++; } } } int kruskal() { int ans = 0; for (int i = 1; i <= n; i++) { make_set(i); } sort(edges, edges + m); for (int i = 0; i < m; i++) { int u = edges[i].u, v = edges[i].v, w = edges[i].w; if (find_set(u) != find_set(v)) { union_sets(u, v); ans += w; } } return ans; } int main() { while (cin >> n >> m >> p) { for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; edges[i] = {u, v, w}; } int ans1 = kruskal(); for (int i = 1; i <= n; i++) { make_set(i); } for (int i = 0; i < p; i++) { int u, v, w; cin >> u >> v >> w; edges[i] = {u, v, -w}; } int ans2 = kruskal(); cout << ans2 - ans1 << endl; } return 0; } ``` 在这个实现中,使用了一个 `Edge` 结构体来表示一条边,包括起、终和边权。然后使用 Kruskal 算法解最小生成树,分别计算突袭前和突袭后的最小生成树的权值和,最终答案为突袭后的权值和减去突袭前的权值和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值