AT_abc412_d 题解

题目传送门

AtCoder

题面

给你一张简单无向图, 要求你做以下两种操作若干次
操作1: 删除一条边
操作2: 选择两个不同的点,增加一条边

请问最少需要操作多少次, 才能使最后每一个节点的度都为2

注: 操作时不可以出现重边或自环(即每一次操作过后这张图仍然是简单无向图)

思路

容易发现: 要使每一个节点的度为\(2\), 那么要形成若干个环(且没有多余边)
非常显然的是: 由于不可以出现重边或自环,所以环的节点数至少为3

继续观察发现: 最后的时候最多有两个环
因为节点数最大为\(8\), 而\(8<3+3+3\), 因此最多有两个环

Q:那么怎么记录所有的环的样子呢?

A:对于每一个环, 我们都可以将其看作为一个长度为环的节点数的数组:

如果数组为\(p_1,p_2,p_3,...,p_n\)

那么这个环就是: \(p_1\)\(p_2\), \(p_2\)\(p_3\),\(p_3\)\(p_4\),...,\(p_{n-1}\)\(p_n\), \(p_n\)\(p_1\)

Q:那么怎么枚举呢?

A:全排列

没错,就是它,它可以枚举数组的所有的排列,每一个排列就表示一个环

就这样了?
Q:等一下! 两个环怎么办?

A:这个好办, 我们只要枚举第一个环的大小即可(从\(3\)枚举到 \(n-3\)), 然后把大数组分成两个小数组, 两个小数组代表了两个小的环

代码:

#include <iostream>
#include <algorithm>
#include <map>
#include <string.h>
using namespace std;
bool graph[9][9];
bool flag[9][9];
int a[29], b[29];
int q[9];
void f(int l, int r) // 构建出从q[l]到q[r]所对应的环。
{
    for (int i = l; i < r; i++)
    {
        flag[q[i]][q[i + 1]] = true;
        flag[q[i + 1]][q[i]] = true;
    }
    flag[q[r]][q[l]] = true;
    flag[q[l]][q[r]] = true;
}
int f2(int n) // 比较两个邻接矩阵有多少个不同的位置,每一个不同的位置都需要将原边删除,或者增加一条边。
{
    int cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++) // 注意j 从i + 1开始,因为不能将一条边多算一次。
        {
            cnt += (flag[i][j] != graph[i][j]);
        }
    }
    return cnt;
}
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> a[i] >> b[i];
        graph[a[i]][b[i]] = graph[b[i]][a[i]] = true; // 记下原图,使用邻接矩阵
    }
    for (int i = 1; i <= n; i++)
    {
        q[i] = i; // 设初始
    }
    int anss = 1e9;
    do
    {
        // 一个环
        memset(flag, 0, sizeof(flag));
        f(1, n); // 构建环
        int cnt1 = 0, cnt2 = 1e9;
        cnt1 = f2(n); // 将原图改为一个环的代价。
        // 两个环
        for (int k = 3; k <= n - 3; k++) // 枚举第一个环的大小
        {
            memset(flag, 0, sizeof(flag));
            int t = 0;
            f(1, k);     // 构建第一个环
            f(k + 1, n); // 构建第二个环
            t = f2(n);  
            cnt2 = min(cnt2, t); // 将原图改为两个环的代价。
        }
        anss = min(anss, min(cnt1, cnt2)); // 比较方案。
    }
    while (next_permutation(q + 1, q + n + 1)); // 改成下一排列。

    cout << anss << endl;
    return 0;
}

posted @ 2025-07-02 22:43  MichaelZeng  阅读(41)  评论(0)    收藏  举报