题意:每个奶牛对所有的牛棚有个排名(根据喜欢程度排的),每个牛棚能够入住的牛的数量有个上限,重新给牛分配牛棚,使牛棚在牛心中的排名差(所有牛中最大排名和最小排名之差)最小。
牛棚个数最多为20,那么直接枚举最差排名和最好排名,对于每种情况判断是否合法,取最小值。
构图:
源点与每头牛之间连接一条边,边权为1,每头牛与枚举范围内的牛棚之间连接一条边,边权为1(表示每头牛可以入住的牛棚),然后每个牛棚与汇点之间建立一条边,边权为每个牛棚的入住上限。
换了一个模板,那个Dinic模板用邻接矩阵存储的,速度太慢了。这个模板非常优秀,只是有点长了。
#include <cstring> #include <algorithm> #include <cstdio> using namespace std; #define N 1200 #define M 50220 #define INF 0x3f3f3f3f class MaxFlow { public: struct record { int v, f, next; } edge[M]; int n, s, t; int pos[N], dis[N], vh[N], cl; int his[N], di[N], pre[N]; void AddEdge(int a, int b, int f) { cl++; edge[cl].next = pos[a]; edge[cl].v = b; edge[cl].f = f; pos[a] = cl; cl++; edge[cl].next = pos[b]; edge[cl].v = a; edge[cl].f = 0; //若为无向边,则f = f pos[b] = cl; } void Init() { cl = 1; memset(dis, 0, sizeof(dis)); memset(vh, 0, sizeof(vh)); memset(pos, 0, sizeof(pos)); } int flow() { vh[0] = n; //初始化GAP数组(默认所有点的距离标号均为0,则距离标号为0的点数量为n) for (int i = 0; i < n; i++) di[i] = pos[i]; //初始化当前弧 int i = s, aug = INF, flow = 0; //初始化一些变量,flow为全局流量,aug为当前增广路的流量 bool flag = false; //标记变量,记录是否找到了一条增广路(若没有找到则修正距离标号) while (dis[s] < n) { his[i] = aug; //保存当前流量 flag = false; for (int p=di[i]; p; p=edge[p].next) if ((edge[p].f > 0) && (dis[edge[p].v] + 1 == dis[i])) {//利用距离标号判定可行弧 flag = true; //发现可行弧 di[i] = p; //更新当前弧 aug = min(aug, edge[p].f); //更新当前流量 pre[edge[p].v] = p; //记录前驱结点 i = edge[p].v; //在弧上向前滑动 if (i == t) {//遇到汇点,发现可增广路 flow += aug; //更新全局流量 while (i != s) {//减少增广路上相应弧的容量,并增加其反向边容量 edge[pre[i]].f -= aug; edge[pre[i]^1].f += aug; i = edge[pre[i]^1].v; } aug = INF; } break; } if (flag) continue; //若发现可行弧则继续,否则更新标号 int min = n - 1; for (int p=pos[i]; p; p=edge[p].next) if ((edge[p].f > 0) && (dis[edge[p].v] < min)) { di[i] = p; //不要忘了重置当前弧 min = dis[edge[p].v]; } --vh[dis[i]]; if (vh[dis[i]] == 0) break; //更新vh数组,若发现距离断层,则算法结束(GAP优化) dis[i] = min + 1; ++vh[dis[i]]; if (i != s) {//退栈过程 i = edge[pre[i]^1].v; aug = his[i]; } } return flow; } } net; int n, b, g[1002][22], s[22]; void init(int x, int y) { net.Init(); net.s = 0, net.t = n+b+1, net.n = n+b+2; for (int i=1; i<=n; i++) net.AddEdge(0, i, 1); for (int i=1; i<=n; i++) for (int j=x; j<=y; j++) net.AddEdge(i, g[i][j]+n, 1); for (int i=1; i<=b; i++) net.AddEdge(n+i, net.t, s[i]); } int main() { scanf("%d%d", &n, &b); for (int i=1; i<=n; i++) for (int j=1; j<=b; j++) scanf(" %d", &g[i][j]); for (int i=1; i<=b; i++) scanf("%d", &s[i]); int ans = INF; for (int i=1; i<=b; i++) for (int j=i; j<=b; j++) { init(i, j); if (net.flow() == n) ans = min(ans, j-i+1); } printf("%d ", ans); return 0; }