Description

一次舞会有 \(n\) 个男孩和 \(n\) 个女孩. 每首曲子开始时, 所有男孩和女孩恰好配成 \(n\) 对跳交谊舞. 每个男孩都不会和同一个女孩跳两首 (或更多) 舞曲. 有一些男孩女孩相互喜欢, 而其他相互不喜欢 (不会 “单向喜欢”) . 每个男孩最多只愿意和 \(k\) 个不喜欢的女孩跳舞, 而每个女孩也最多只愿意和 \(k\) 个不喜欢的男孩跳舞. 给出每对男孩女孩是否 相互喜欢的信息, 舞会最多能有几首舞曲?

Input

第一行包含两个整数 \(n\)\(k\). 以下 \(n\) 行每行包含 \(n\) 个字符, 其中第 \(i\) 行第 \(j\) 个字符为 Y 当且仅当男孩 \(i\) 和女孩 \(j\) 相互喜欢.

Output

仅一个数, 即舞曲数目的最大值.

Sample Input

3 0
YYY
YYY
YYY

Sample Output

3

Data Range

对于所有数据, 保证:\(n \leq 50, k \leq 30\)

Explanation

一道网络流题, 有一个位置没有想清楚建边建反了所以 WA 了一次

引用 @hzwer 大神的题解一弹:

先把每个人 \(i\) 拆分成 \(i_x\)\(i_y\) 两个节点,\(i_x\) 连向喜欢的人,\(i_y\) 连向不喜欢的人, 容量为 \(1\) (比如如果男生 i 与女生 j 互相喜欢, 则由 \(i_x\) 连向 \(j_x\), 如果 男生 \(i\) 与女生 \(j\) 互相不喜欢, 则由 \(i_y\) 连向 \(j_y\)) , 再将每个男生 \(i_x\) 连向 \(i_y\), 容量为 \(k\); 每个女生 \(i_y\) 连向 \(i_x\), 容量为 \(k\). 由源点向每个男生的 \(x\) 节点连上一条边, 再由每个女生的 \(x\) 节点向汇点连上一条边, 容量均为 \(a\). 最后 从小到大枚举 \(a\), 计算最大流 \(flow\), 若发现 \(a \cdot n \leq flow\) (不满流), 则停止枚举,\(a - 1\) 即为答案.

(或者可以二分)

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>

#define rep(_var,_begin,_end) for(int _var=_begin;_var<=_end;_var++)
using namespace std;
typedef long long lli;
const int maxN = 60, maxn = 1010, maxm = 500100;
const int infinit = 0x003f3f3f;

bool get_alpha_char(void) { char ch = '\0';
    while (ch != 'Y' && ch != 'N') ch = getchar();
    return ch == 'Y'; }

class Dinic
{
public:
    struct edge
    {
        int u, v, flow;
        edge *next, *rev;
    };
    int n, s, t, ecnt, depth[maxn];
    edge *edges[maxn], epool[maxm];
    void add_edge(int u, int v, int flow)
    {
        edge *p = &epool[++ecnt],
             *q = &epool[++ecnt];
        p->u = u; p->v = v; p->flow = flow;
        p->next = edges[u]; edges[u] = p; p->rev = q;
        q->u = v; q->v = u; q->flow = 0;
        q->next = edges[v]; edges[v] = q; q->rev = p;
        return ;
    }
    bool make_depth(void)
    {
        memset(depth, 0, sizeof(depth));
        queue<int> que;
        que.push(s);
        depth[s] = 1;
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (edge *ep = edges[p]; ep; ep = ep->next)
                if (ep->flow && !depth[ep->v]) {
                    depth[ep->v] = depth[p] + 1;
                    que.push(ep->v);
                }
            if (depth[t] > 0)
                return true;
        }
        return false;
    }
    int find(int p, int mn)
    {
        if (p == t)
            return mn;
        int tmp = 0, sum = 0;
        for (edge *ep = edges[p]; ep && sum < mn; ep = ep->next)
            if (depth[ep->v] == depth[p] + 1 && ep->flow) {
                tmp = find(ep->v, min(mn - sum, ep->flow));
                if (tmp > 0) {
                    sum += tmp;
                    ep->flow -= tmp;
                    ep->rev->flow += tmp;
                }
            }
        if (sum <= 0)
            depth[p] = 0; // Eject
        return sum;
    }
    int eval(void)
    {
        int tmp, sum = 0;
        while (make_depth()) {
            tmp = find(s, infinit);
            if (tmp <= 0)
                break;
            sum += tmp;
        }
        return sum;
    }
    void clear(void)
    {
        memset(edges, 0, sizeof(edges));
        n = s = t = ecnt = 0;
        return ;
    }
} graph;

int n, K;
bool arr[maxN][maxN];

bool judge_flow(int flow_limit)
{
    graph.clear();
    graph.n = 1001;
    graph.s = 0;
    graph.t = 1001;
    // Linking from source to favourable boys
    rep(i, 1, n) graph.add_edge(graph.s, i, flow_limit);
    // Linking from favourable boys to unfavourable boys
    rep(i, 1, n) graph.add_edge(i, 500 + i, K);
    // Linking from favourable girls to target
    rep(i, 1, n) graph.add_edge(500 + n + i, n + i, K);
    // Linking from favourable girls to unfavourable girls
    rep(i, 1, n) graph.add_edge(n + i, graph.t, flow_limit);
    // Linking from boys to girls
    rep(i, 1, n) rep(j, 1, n) {
        if (arr[i][j]) graph.add_edge(i, n + j, 1);
        else graph.add_edge(500 + i, 500 + n + j, 1);
    }
    // Built graph. Run max flow.
    int res = graph.eval();
    // Check delimiations
    if (res < n * flow_limit)
        return false;
    return true;
}

int main(int argc, char** argv)
{
    scanf("%d%d", &n, &K);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            arr[i][j] = get_alpha_char();
    // Binary searching.
    int l = 0, r = 50;
    int res = 0;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (judge_flow(mid)) {
            res = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    // Outputting result
    printf("%d\n", res);
    return 0;
}