let menu = ["Home", "Algorithms", "CodeHub", "VNOI Statistics"];

Overview

VOI 2012 TRAVEL12 - Hành trình du lịch

solution.md

Bài này ta cần chú ý là giữa 2 đỉnh có thể có nhiều hơn một đường đi. Ngoài ra, chỉ cần duyệt như sau:

Với mỗi đỉnh \(u\), ta xét các đỉnh kề của \(u\) và xem trong các đỉnh kề đó có 2 đỉnh nào cùng kề với một đỉnh khác \(u\) hay không, nếu có thì ta in kết quả.

Độ phức tạp của thuật toán là \(O(m \times n)\), nhưng do ta sử dụng cấu trúc bitset nên thuật toán sẽ chạy trong thời gian cho phép. Trên thực tế, số thao tác cần thực hiện khoảng \(m \times n / 64\).

Xem comment trong code để biết thêm chi tiết.

Ngoài ra còn có cách làm \(O(n^2)\) do một bạn đề xuất như sau:

Với mỗi đỉnh u từ 1 đến n, duyệt tất cả các cặp đỉnh (v1, v2) kề với u, nếu f[v1][v2] chưa được đánh dấu thì tiến hành đánh dấu f[v1][v2], ngược lại thì ta tiến hành duyệt một lần nữa để tìm lại đỉnh v đã đánh dấu f[v1][v2], khi đó ta có chu trình u -> v1 -> v -> v2 -> u. Để tiết kiệm bộ nhớ thì ta dùng bitset để biểu diễn f.

Tuy nhiên, với giới hạn của bài này thì cách làm \(O(mn)\) bên trên nhanh hơn.

main.cpp
Open in Github Download
#include <iostream>
#include <vector>
#include <bitset>
using namespace std;

typedef vector<vector<int>> dsk;
typedef bitset<10001> bs;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int n, m; cin >> n >> m;
    dsk ke(n + 1);
    // bits[u] sẽ có giá trị true tại các vị
    // trí tương ứng với các đỉnh kề với u
    vector<bs> bits(n + 1);
    while (m--) {
        int u, v; cin >> u >> v;
        if (!bits[u].test(v)) {
            ke[u].push_back(v);
            ke[v].push_back(u);
            bits[v].set(u);
            bits[u].set(v);
        }
    }
    for (int u=1; u<=n; u++) {
        // f lưu các đỉnh kề với các đỉnh kề của u
        bs f;
        for (int v: ke[u]) {
            bits[v][u] = false;

            // lệnh dưới kiểm tra xem f và bits[v]
            // có chung bit 1 nào không.
            if ((f & bits[v]).any()) {
                // trường hợp có ít nhất 1 bit 1 chung,
                // ta sẽ có được chu trình:
                //     u
                //    / \
                //   x   v
                //    \ /
                //     w
                // truy vết tìm w và x, sau đó xuất kết quả
                f &= bits[v];
                int w = 1;
                while (!f[w]) w++;
                for (int x: ke[w]) if (bits[u][x]) {
                    cout << u << " " << v << " " << w << " " << x;
                    return 0;
                }
            } else {
                // cập nhật lại f
                f |= bits[v];
            }

            bits[v][u] = true;
        }
    }
    cout << -1;
    return 0;
}
Comments