题解P3153 [CQOI2009]跳舞

读完这到题,感觉上像是一道二分图匹配得问题,但是它对于每一个点又有一些限制,所以可以考虑拆点,将男生和女生都拆成两个点,并连一条容量为k的边
题解P3153 [CQOI2009]跳舞

对于一对相互喜欢的男女,在男l与女r之间连一条容量为1的边,如果不喜欢那么就在男r与女l之间连一条容量为1的边,枚举可以有和舞曲数量m,在S与男l,女r与T之间连一条容量为m的边,如果该流网络存在满流的最大流,那么这个答案就是合法的,因为此题答案并不大,所以可以直接枚举

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1005, M = 1e5 + 5, INF = 1e8;

int n, k, idx, S, T;
int head[N], ver[M], net[M], cpt[M];
int d[N], cur[N], q[N];

void add(int a, int b, int c)
{
    net[idx] = head[a], ver[idx] = b, cpt[idx] = c, head[a] = idx++;
    net[idx] = head[b], ver[idx] = a, cpt[idx] = 0, head[b] = idx++;
}

bool bfs()
{
    int front = 0, tail = 0;
    memset(d, -1, sizeof(d));
    q[0] = S, d[S] = 0, cur[S] = head[S];
    while (front <= tail)
    {
        int u = q[front++];
        for (int i = head[u]; ~i; i = net[i])
        {
            int v = ver[i];
            if (d[v] == -1 && cpt[i])
            {
                d[v] = d[u] + 1;
                cur[v] = head[v];
                if (v == T)
                    return true;
                q[++tail] = v;
            }
        }
    }
    return false;
}

int find(int u, int limit)
{
    if (u == T)
        return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = net[i])
    {
        cur[u] = i;
        int v = ver[i];
        if (d[v] == d[u] + 1 && cpt[i])
        {
            int x = find(v, min(limit - flow, cpt[i]));
            if (!x)
                d[v] = -1;
            cpt[i] -= x, cpt[i ^ 1] += x, flow += x;
        }
    }
    return flow;
}

int dinic()
{
    int flow, res = 0;
    while (bfs())
    {
        while (flow = find(S, INF))
            res += flow;
    }
    return res;
}

int main()
{
    memset(head, -1, sizeof(head));
    S = 0, T = N - 1;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            char a;
            scanf(" %c", &a);
            if (a == 'Y')
                add(i, 3 * n + j, 1);
            else
                add(n + i, 2 * n + j, 1);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        add(i, n + i, k), add(2 * n + i, 3 * n + i, k);
        add(S, i, 0), add(3 * n + i, T, 0);
    }
    int ans = 55;
    while(ans--)
    {
        for (int i = 0; i < idx; i += 2)
        {            
            if (cpt[i ^ 1])
                cpt[i] += cpt[i ^ 1], cpt[i ^ 1] = 0;
            if (ver[i ^ 1] == S || ver[i] == T)
                cpt[i] = ans;
        }
        if (dinic() == ans * n)
        {
            printf("%d", ans);
            return 0;
        }
    }
    printf("%d", ans);
    return 0;
}
上一篇:费用流模板


下一篇:圆桌问题(最大流,二分图,网络流24题)