【BFS】【最小生成樹】Petrozavodsk Winter Training Camp 2018 Day 1: Jagiellonian U Contest, Tuesday, January 3

題意:給你n個點,點帶權,任意兩點之間的邊權是它們的點權的異或值中「1」的個數,問你該圖的最小生成樹。ios

看似是個徹底圖,實際上有不少邊是廢的。相似……卡諾圖的思想?從讀入的點出發BFS,每次只到改變它的任意一位所能到達的點(不管是否讀入)。c++

記錄每一個點是從哪一個讀入點BFS過來的,當第二次訪問某個點的時候,就將它的兩個源頭(一次是第一次的時候標記的,一次是第二次過來的)連一條邊。spa

這樣最多連m(位數)*n條邊,實際上比這個值更小。blog

這種作法能夠將不少顯然不會出如今最小生成樹裏的邊排除掉。ci

opencup的標程:string

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
// O(kn)

#include <bits/stdc++.h>

using namespace std;


struct FAU {
    vector <int> p, r;

    FAU(int n): p(n,-1), r(n,0) {}

    int find(int x) {
        if (p[x] == -1) return x;
        return p[x] = find(p[x]);
    }

    void join(int x, int y) {
        x = find(x); y = find(y);
        if (x == y) return ;

        if (r[x] > r[y]) p[y] = x;
        else p[x] = y;

        if (r[x] == r[y]) ++r[y];
    }
};

int readBinary() {
    string s;
    cin >> s;
    
    int result = 0;
    for (char c : s) {
        result = 2 * result + ((c == 'L') ? 1 : 0);
    }
    
    return result;
}

void solveTestcase() {
    int k, n;
    cin >> k >> n;

    const int N = 1 << k;
    
    vector <int> dist(N, -1), from(N);
    queue <int> q;
    
    for (int i = 0; i < n; i++) {
        int val = readBinary();
        
        dist[val] = 0;
        from[val] = i;
        
        q.push(val);
    }
    
    vector <vector <pair<int,int>>> edges(k+1);
    
    while (!q.empty()) {
        int v = q.front(); q.pop();
        
        for (int bit = 0; bit < k; bit++) {
            int u = v ^ (1 << bit);
            
            if (dist[u] == -1) {
                dist[u] = 1 + dist[v];
                from[u] = from[v];
                
                q.push(u);
            } else if (from[u] != from[v]) {
                int len = dist[u] + dist[v] + 1;
                
                if (len <= k) {
                    edges[len].push_back({from[u], from[v]});
                }
            }
        }
    }
    
    FAU fau(n);
    int ans = 0;
    
    for (int len = 1; len <= k; len++) for (auto &edge : edges[len]) {
        if (fau.find(edge.first) != fau.find(edge.second)) {
            ans += len;
            fau.join(edge.first, edge.second);
        }
    }
    
    cout << ans << '\n';
}

int main() {
    ios_base::sync_with_stdio(false);
    
    int z;
    cin >> z;
    
    while (z--) {
        solveTestcase();
    }
    
    return 0;
}
相關文章
相關標籤/搜索