首页 > 试题广场 >

二分 K-means子网分割

[编程题]二分 K-means子网分割
  • 热度指数:26 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
在网络规划中,希望把若干二维站点拆分为多个子网,便于运维。给定期望子网数 N 和所有站点坐标,采用“二分 K-means”自顶向下的思路:从一个簇开始,每次只把当前 SSE(簇内点到簇心的平方和)最大的簇再二分为两个簇,直到簇数达到 N。二分时使用标准 K=2 的 K-means:初始两个簇心选该簇中 x 坐标最小点与 x 坐标最大点(如有并列,按 y 再按输入次序打破并列),迭代更新“按欧氏距离最近分配 + 以簇内平均坐标为新簇心”,当簇心总移动量均小于 1e-6 或迭代满 1000 次即认为收敛;若出现某一类空簇,则保持该簇心不变继续迭代。每次二分后输出当前所有簇的规模(站点数),按降序排列;共输出 N−1 行。

输入描述:
第一行:N(目标簇数,整数)
第二行:M(站点数,整数)
接下来 M 行:每行两个整数 x y(0≤x,y≤1000)


输出描述:
共输出 N−1 行。第 k 行为完成第 k 次二分后的各簇规模(降序),以空格分隔
示例1

输入

3
5
0 0
1 0
10 0
11 0
12 0

输出

3 2
2 2 1

说明

首次把点大致按中间位置分成 {0,1} 与 {10,11,12},规模为 2 与 3;第二次优先二分规模更大且 SSE 更大的簇 {10,11,12} 为 {10} 与 {11,12},此时三个簇规模降序为 2 2 1。
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
struct group {
    double SSE;
    vector<int>members;//保证升序
    group(double SSE, const vector<int>& members): SSE(SSE), members(members) {}
    bool operator<(const group& other)const {
        if (members.size() == other.members.size()) {
            return SSE < other.SSE;
        }
        return members.size() < other.members.size();
    }
};
vector<pair<int, int>>a;
bool cmp(const int p1, const int p2) {
    if (a[p1].first == a[p2].first) {
        return a[p1].second < a[p2].second;
    }
    return a[p1].first < a[p2].first;
}
long long dist2(int i, int j) {
    return (a[i].first - a[j].first) * (a[i].first - a[j].first) +
           (a[i].second - a[j].second) * (a[i].second - a[j].second);
}
double getSSE(vector<int>& mem) {
    double x = 0;
    double y = 0;
    for (int i : mem) {
        x += a[i].first;
        y += a[i].second;
    }
    x /= mem.size();
    y /= mem.size();
    double SSE = 0;
    for (int i : mem) {
        SSE += (a[i].first - x) * (a[i].first - x) + (a[i].second - y) *
               (a[i].second - y);
    }
    return SSE;
}
int main() {
    int n, m;
    cin >> n >> m;
    a.resize(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i].first >> a[i].second;
    }
    //交换顺序不影响结果
    sort(a.begin(), a.end());
    vector<int>tmp;
    for (int i = 0; i < m; i++) {
        tmp.push_back(i);
    }
    priority_queue<group>pq;
    map<int, int> flag;
    flag[m]++;
    pq.push(group(0, tmp));
    vector<vector<int>>ans;
    while (pq.size() < n) {
        group now = pq.top();
        pq.pop();
        int a1 = now.members[0];
        int a2 = now.members[now.members.size() - 1];
        flag[now.members.size()]--;
        if (flag[now.members.size()] == 0) {
            flag.erase(now.members.size());
        }
        vector<int>mem1, mem2;
        mem1.push_back(a1);
        for (int i = 1; i < now.members.size() - 1; i++) {
            long long d1 = dist2(i, a1);
            long long d2 = dist2(i, a2);
            if (d1 < d2) {
                mem1.push_back(i);
            } else {
                mem2.push_back(i);
            }
        }
        mem2.push_back(a2);
        pq.push(group(getSSE(mem1), mem1));
        pq.push(group(getSSE(mem2), mem2));
        flag[mem1.size()]++;
        flag[mem2.size()]++;
        vector<int>tmp;//从小到大
        for (auto& pair : flag) {
            for (int i = 0; i < pair.second; i++)
                tmp.push_back(pair.first);
        }
        ans.push_back(tmp);
    }
    for (int i = 0; i < ans.size(); i++) {
        //倒序输出
        for (int j = ans[i].size() - 1; j >= 0; j--) {
            cout << ans[i][j] << " ";
        }
        cout << "\n";
    }
}

发表于 今天 00:40:20 回复(0)
#include <bits/stdc++.h>
using namespace std;

const int MAXP = 100010; // 最大点数
const int MAXC = 50;     // 最大簇数(N ≤ 50假设)

double px[MAXP], py[MAXP]; // 点坐标
int cluster_members[MAXC][MAXP]; // 每簇的成员点下标
int cluster_size[MAXC]; // 每簇成员数
double center_x[MAXC], center_y[MAXC], sse[MAXC]; // 簇心 & SSE

int N, M; // 簇目标数量 & 点数量

void compute_center_sse(int cid) {
    double sumx = 0, sumy = 0;
    for (int i = 0; i < cluster_size[cid]; ++i) {
        int pid = cluster_members[cid][i];
        sumx += px[pid];
        sumy += py[pid];
    }
    center_x[cid] = sumx / cluster_size[cid];
    center_y[cid] = sumy / cluster_size[cid];

    double total_sse = 0;
    for (int i = 0; i < cluster_size[cid]; ++i) {
        int pid = cluster_members[cid][i];
        double dx = px[pid] - center_x[cid];
        double dy = py[pid] - center_y[cid];
        total_sse += dx*dx + dy*dy;
    }
    sse[cid] = total_sse;
}

// 二分 K-means 分裂簇 cid -> 新簇 cid1, cid2
bool split_cluster(int cid, int cid1, int cid2) {
    if (cluster_size[cid] <= 1) return false;

    // 找 x最小和最大点
    double minx = px[cluster_members[cid][0]], maxx = minx;
    int min_id = cluster_members[cid][0], max_id = min_id;
    for (int i = 1; i < cluster_size[cid]; ++i) {
        int pid = cluster_members[cid][i];
        if (px[pid] < minx) { minx = px[pid]; min_id = pid; }
        if (px[pid] > maxx) { maxx = px[pid]; max_id = pid; }
    }

    double cx1 = px[min_id], cy1 = py[min_id];
    double cx2 = px[max_id], cy2 = py[max_id];

    for (int it = 0; it < 1000; ++it) {
        cluster_size[cid1] = 0;
        cluster_size[cid2] = 0;

        // 分配
        for (int i = 0; i < cluster_size[cid]; ++i) {
            int pid = cluster_members[cid][i];
            double d1 = (px[pid]-cx1)*(px[pid]-cx1)+(py[pid]-cy1)*(py[pid]-cy1);
            double d2 = (px[pid]-cx2)*(px[pid]-cx2)+(py[pid]-cy2)*(py[pid]-cy2);
            if (d1 <= d2) cluster_members[cid1][cluster_size[cid1]++] = pid;
            else cluster_members[cid2][cluster_size[cid2]++] = pid;
        }

        if (cluster_size[cid1] == 0 || cluster_size[cid2] == 0) return false;

        // 更新簇心
        double sumx1 = 0, sumy1 = 0;
        for (int i = 0; i < cluster_size[cid1]; ++i) {
            int pid = cluster_members[cid1][i];
            sumx1 += px[pid]; sumy1 += py[pid];
        }
        double new_cx1 = sumx1 / cluster_size[cid1];
        double new_cy1 = sumy1 / cluster_size[cid1];

        double sumx2 = 0, sumy2 = 0;
        for (int i = 0; i < cluster_size[cid2]; ++i) {
            int pid = cluster_members[cid2][i];
            sumx2 += px[pid]; sumy2 += py[pid];
        }
        double new_cx2 = sumx2 / cluster_size[cid2];
        double new_cy2 = sumy2 / cluster_size[cid2];

        double move = sqrt((new_cx1-cx1)*(new_cx1-cx1)+(new_cy1-cy1)*(new_cy1-cy1))
                    + sqrt((new_cx2-cx2)*(new_cx2-cx2)+(new_cy2-cy2)*(new_cy2-cy2));
        cx1 = new_cx1; cy1 = new_cy1;
        cx2 = new_cx2; cy2 = new_cy2;

        if (move <= 1e-6) break;
    }

    compute_center_sse(cid1);
    compute_center_sse(cid2);
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> N >> M;
    for (int i = 0; i < M; ++i) {
        cin >> px[i] >> py[i];
    }

    int cluster_count =1;
    cluster_size[0] = M;
    for (int i = 0; i < M; ++i) cluster_members[0][i] = i;
    compute_center_sse(0);

    while (cluster_count < N) {
        // 找 SSE 最大的簇
        int best_idx = 0;
        for (int i = 1; i < cluster_count; ++i) {
            if (sse[i] > sse[best_idx]) best_idx = i;
        }
        int new_cid1 = cluster_count++;
        int new_cid2 = cluster_count++;

        if (!split_cluster(best_idx, new_cid1, new_cid2)) {
            cluster_count -= 2; // 分裂失败
            break;
        }

        // 删除 old cluster
        cluster_size[best_idx] = cluster_size[new_cid1];
        for (int i = 0; i < cluster_size[new_cid1]; ++i) {
            cluster_members[best_idx][i] = cluster_members[new_cid1][i];
        }
        center_x[best_idx] = center_x[new_cid1];
        center_y[best_idx] = center_y[new_cid1];
        sse[best_idx] = sse[new_cid1];

        // 最后一个簇改成 new_cid2
        cluster_size[new_cid1] = cluster_size[new_cid2];
        for (int i = 0; i < cluster_size[new_cid2]; ++i) {
            cluster_members[new_cid1][i] = cluster_members[new_cid2][i];
        }
        center_x[new_cid1] = center_x[new_cid2];
        center_y[new_cid1] = center_y[new_cid2];
        sse[new_cid1] = sse[new_cid2];
        cluster_count--; // 实际增加了1簇

        // 输出簇大小降序
        int sizes[MAXC];
        for (int i = 0; i < cluster_count; ++i) sizes[i] = cluster_size[i];
        sort(sizes, sizes+cluster_count, greater<int>());
        for (int i = 0; i < cluster_count; ++i) {
            cout << sizes[i] << (i+1 == cluster_count ? '\n' : ' ');
        }
    }
    return 0;
}

发表于 2025-10-16 22:36:46 回复(0)