读完这到题,感觉上像是一道二分图匹配得问题,但是它对于每一个点又有一些限制,所以可以考虑拆点,将男生和女生都拆成两个点,并连一条容量为k的边
对于一对相互喜欢的男女,在男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;
}