[NOIP2018 提高組] 旅行

Yorg發表於2024-11-09

演算法

對於一棵樹的情況, dfs + 貪心選取顯然是正確的
對於基環樹的情況
我們觀察到城市不能重複行走
所以長為 \(L\) 的環最多隻會被訪問 \(L - 1\) 條邊
列舉斷邊, 再跑 dfs + 貪心即可

程式碼

#include <bits/stdc++.h>
const int MAXN = 5e3 + 20;

int n, m;
std::vector<int> e[MAXN], ans;

int U[MAXN], V[MAXN];

class Subtast_1
{
private:

public:
    bool vis[MAXN];
    int res[MAXN];
    void dfs(int u)
    {
        ans.push_back(u), vis[u] = true;
        for (int i = 0; i < e[u].size(); ++i)
        {
            int v = e[u][i];
            if (!vis[v])
                dfs(v);
        }
    }

    void solve()
    {
        dfs(1);
        for (int i = 0; i < ans.size(); ++i)
            std::cout << ans[i] << " ";
    }
} s1;

class Subtask_2
{
private:

public:
    int res[MAXN], ans[MAXN];
    bool cmp()
    {
        for (int i = 1; i <= n; ++i)
            if (res[i] < ans[i])
                return true;
            else if (res[i] > ans[i])
                return false;
        return false;
    }

    int Delete_u, Delete_v, Past_Cnt = 0;
    bool NotbeDelete(int u, int v)
    {
        if ((u == Delete_u && v == Delete_v) || (u == Delete_v && v == Delete_u))
            return false;
        return true;
    }

    bool vis[MAXN];
    void dfs(int u)
    {
        vis[u] = true, res[++Past_Cnt] = u;
        for (int i = 0; i < e[u].size(); ++i) {
            int v = e[u][i];
            /*不跑刪邊*/
            if (!vis[v] && NotbeDelete(u, v))
                dfs(v);
        }
    }

    void solve()
    {
        for (int i = 1; i <= m; ++i)
        {
            int u = U[i], v = V[i];

            memset(res, 0, sizeof(res));
            memset(vis, false, sizeof(vis));
            Past_Cnt = 0;
            Delete_u = u, Delete_v = v;

            /*判斷刪掉的這條邊在不在環上*/
            dfs(1);
            if (Past_Cnt < n)
                continue;

            bool Best = cmp();
            if (ans[1] == 0 || Best)
                memcpy(ans, res, sizeof(res));
        }

        for (int i = 1; i <= n; ++i)
            std::cout << ans[i] << " ";
    }
} s2;
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1, u, v; i <= m; ++i) {
        scanf("%d %d", &u, &v);
        e[u].push_back(v), e[v].push_back(u), U[i] = u, V[i] = v;
    }
    for (int i = 1; i <= n; ++i)
        sort(e[i].begin(), e[i].end());

    if (m == n - 1)
        s1.solve();
    else if (m == n)
        s2.solve();

    return 0;
}

總結

斷邊後, 基環樹可按照樹的方式處理

相關文章