1. 程式人生 > >洛谷 P4258 UOJ #171 【WC2016】挑戰NPC

洛谷 P4258 UOJ #171 【WC2016】挑戰NPC

題解

建模:

把每個框子拆成三個點並連在一起,每顆球向他能匹配的框子的三個點分別連邊。
之後求一般圖最大匹配即可

證明:

如果原問題取得最優解,每顆球都會與一個框匹配,每個框如果匹配的球數不超過兩個,則內部產生一條匹配邊,這是一個最大匹配。
反過來,任意最大匹配中,每顆球必然能匹配,而後框子的“內部匹配邊”會盡可能多,所以任意最大匹配一定是如上所述的一個匹配。

程式碼

#include<bits/stdc++.h>

using namespace std;

const int maxm = 100 + 5;
const int maxn = maxm * 6;
const int maxe = 500000 + 5;

int t;
int n, m, e;
int head[maxn], nxt[maxe * 2], to[maxe * 2], cnt;
void inline addEdge(int u, int v) {
    nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt;
    nxt[++cnt] = head[v], to[cnt] = u, head[v] = cnt;
}

namespace Blossom
{
    int pre[maxn];
    int find(int x) { return pre[x] ? pre[x] = find(pre[x]) : x; }
    int type[maxn], que[maxn], front, tail, link[maxn], match[maxn], deep[maxn], fa[maxn];
    
    int inline lca(int u, int v) {
        u = find(u), v = find(v);
        while(u != v) {
            if(deep[u] > deep[v]) u = find(fa[u]);
            else v = find(fa[v]);
        }
        return u;
    }
    
    void inline shrink(int u, int v, int e) {
        while(find(u) != e) {
            link[u] = v, v = match[u];
            if(type[v] == 2) type[v] = 1, que[tail++] = v;
            if(find(u) == u) pre[u] = e;
            if(find(v) == v) pre[v] = e;
            u = link[v];
        }
    }
    
    bool inline bfs(int s)
    {
        memset(pre, 0, sizeof pre); 
        memset(type, 0, sizeof type); 
        memset(link, 0, sizeof fa);
        memset(fa, 0, sizeof fa);
        front = 0, tail = 0;
        type[que[tail++] = s] = 1;
        deep[s] = 1; fa[s] = 0;
        while(front != tail) 
        {
            int u = que[front++];
            for(int e = head[u]; e; e = nxt[e]) 
            {
                int v = to[e];
                if(find(v) == find(u) || type[v] == 2) continue;
                if(!type[v]) {
                    deep[v] = deep[u] + 1, fa[v] = u;
                    type[v] = 2, link[v] = u;
                    if(!match[v]) {
                        for(int now = v, last; now; now = last) {
                            last = match[link[now]];
                            match[now] = link[now], match[link[now]] = now;
                        }
                        return true;
                    }
                    type[match[v]] = 1, que[tail++] = match[v]; deep[match[v]] = deep[v] + 1, fa[match[v]] = v;
                } else if(type[v] == 1){
                    int e = lca(u, v);
                    shrink(u, v, e);
                    shrink(v, u, e);
                }
            }
        }
        
        return false;
    }
    
    int inline edmond() {
        int ans = 0;
        memset(match, 0, sizeof match);
        for(int i = 1; i <= n + 3 * m; ++i) if(!match[i]) ans += bfs(i);
        return ans;
    }
}

using namespace Blossom;

void inline Init()
{
    memset(head, 0, sizeof head);
    memset(to, 0, sizeof to);
    memset(nxt, 0, sizeof nxt);
    cnt = 0;
    scanf("%d %d %d", &n, &m, &e);
    int u, v;
    while(e--) {
        scanf("%d %d", &u, &v);
        addEdge(u, 3 * (v - 1) + n + 1);
        addEdge(u, 3 * (v - 1) + n + 2);
        addEdge(u, 3 * (v - 1) + n + 3);
    }
    for(int i = 1; i <= m; ++i) {
        int a = 3 * (i - 1) + n + 1, b = 3 * (i - 1) + n + 2, c = 3 * (i - 1) + n + 3;
        addEdge(a, b);
        addEdge(b, c);
        addEdge(c, a);
    }
}

int main()
{
    scanf("%d\n", &t);
    while(t--) {
        Init();
        printf("%d\n", edmond() - n);
        for(int i = 1; i <= n; ++i) printf("%d ", (match[i] - n - 1) / 3 + 1);
        putchar('\n');
    }
    return 0;
}