HDU5727 Necklace(全排列+二分圖匹配)

HDU5727 Necklace(全排列+二分圖匹配)

題面

題意:有 2N2*N 個珠子,其中 NN 個是陰性的,NN 個是陽性的。現在需要把這些珠子串成一個項鍊,頭尾相接且必須陰陽相間,但是有些陽性的珠子與某些陰性的珠子相鄰的時候會讓該陽性珠子變得黯淡,問製作出項鍊後至少有多少個黯淡陽性珠子。

範圍0N9  0MNN0 \le N \le 9~,~0 \le M \le N*N

分析:一開始看到數據範圍這麼小,想着直接進行全排列求解的,後面發現題目看錯了,還以爲頂多 9!9! 種情況,有 2N2*N 個珠子,如果全排列,則有 18!18! 種情況,這不血T。

既然不能對所有的珠子進行全排列,不過可以只對其中一種珠子進行全排列,9!=3628809! = 362880, 那麼我們問題就是如何在剩下的位置中放置陽性珠子才能讓黯淡陽性珠子最少。而該問題又容易聯繫到二分圖匹配問題,把每個位置當成一個點,向能夠放置在該位置的的非陽性珠子連線,之後跑最大匹配,此時最少黯淡陽性珠子數 = NN - 最大匹配數。

這樣我們就求出所有情況下的最少黯淡陽性珠子數,取最小值即可。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

const int MAXN = 10 + 10;
const int MAXM = MAXN * MAXN;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);

int n, m;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

struct Edge
{
    int to, next;
} edge[MAXM];

int head[MAXN], tot;

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
    return;
}

void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    return;
}

int linker[MAXN * 2];
bool used[MAXN * 2];
int uN;

bool dfs(int u)
{
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if (!used[v])
        {
            used[v] = true;
            if (linker[v] == -1 || dfs(linker[v]))
            {
                linker[v] = u;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int res = 0;
    memset(linker, -1, sizeof(linker));
    for (int u = 1; u <= uN; u++) //  點的編號0~uN-1
    {
        memset(used, false, sizeof(used));
        if (dfs(u))
        {
            res++;
        }
    }
    return res;
}

int g[MAXN][MAXN];
int Yin[MAXN];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    while (cin >> n >> m)
    {
        if (n == m && n == 0)  // 一定要特判!
        {
            cout << 0 << endl;
            continue;
        }
        // 注意清空數組
        memset(g, 0, sizeof(g));
        uN = n;
        for (int i = 0; i < m; i++)
        {
            int u, v;
            cin >> u >> v;
            g[u][v] = 1;
        }
        // 全排列前進行賦值
        for (int i = 1; i <= n; i++)
        {
            Yin[i] = i;
        }
        int ans = INF;
        do
        {
            // 注意每次全排列都要初始化
            init();
            for (int i = 1; i <= n; i++)
            {
                int a = i, b = i + 1;
                if (a == n)
                    b = 1;
                for (int j = 1; j <= n; j++)
                {
                    // 跟兩側的陰性珠子都不衝突則連線
                    if (g[j][Yin[a]] || g[j][Yin[b]])
                        continue;
                    addedge(i, n + j);
                }
            }
            // 求最小黯淡陽性珠子數
            ans = min(ans, n - hungary());
        } while (next_permutation(Yin + 2, Yin + 1 + n)); // 注意是+2
        cout << ans << endl;
    }
    return 0;
}

【END】感謝觀看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章