这个题目是说,有n个女的和男的找伴侣。然后女的具有主动选择权,每个女的可以选自己喜欢的男的,也可以挑选k个不喜欢的男的,做法就是:把女的拆点,u1->u2建立一条容量为k的边。如果遇见喜欢的男生i->j+2*n建一条容量为1的边,否则i+n->j+2*n建一条容量为1的边。最后将源点和女生相连容量为mid,汇点与男生相连容量为mid。枚举mid,看是否会产生满流。
可能姿势不够优美dinic超时了啊,换成SAP快了很多啊、、、
1 4 5 1 2 1 1 2 3 3 2 4 2 4 4 1 4 2 3
3
#include <algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <iomanip> #include <stdio.h> #include <string> #include <queue> #include <cmath> #include <stack> #include <map> #include <set> #define eps 1e-12 ///#define M 1000100 #define LL __int64 ///#define LL long long ///#define INF 0x7ffffff #define INF 0x3ffffff #define PI 3.1415926535898 #define zero(x) ((fabs(x)<eps)?0:x) using namespace std; const int maxn = 1010; ///int deep[maxn]; int cnt; int n, m, k; int S, T; int fa[maxn]; int vis[maxn][maxn]; int cur[maxn], head[maxn]; int dis[maxn], gap[maxn]; int aug[maxn], pre[maxn]; struct node { int v, w; int next; } f[510000]; struct node1 { int l, r; } pp[50010]; int Find(int x) { if(x != fa[x]) fa[x] = Find(fa[x]); return fa[x]; } void Union(int x,int y) { int xx,yy; xx=Find(x); yy=Find(y); if(xx!=yy) fa[xx]=yy; } void init() { cnt = 0; memset(head, -1, sizeof(head)); for(int i = 1; i <= n; i++) fa[i] = i; } void add(int u, int v, int w) { f[cnt].v = v; f[cnt].w = w; f[cnt].next = head[u]; head[u] = cnt++; f[cnt].v = u; f[cnt].w = 0; f[cnt].next = head[v]; head[v] = cnt++; } int SAP(int s, int e, int n) { int max_flow = 0, v, u = s; int id, mindis; aug[s] = INF; pre[s] = -1; memset(dis, 0, sizeof(dis)); memset(gap, 0, sizeof(gap)); gap[0] = n; for (int i = 0; i <= n; ++i) cur[i] = head[i];/// 初始化当前弧为第一条弧 while (dis[s] < n) { bool flag = false; if (u == e) { max_flow += aug[e]; for (v = pre[e]; v != -1; v = pre[v]) /// 路径回溯更新残留网络 { id = cur[v]; f[id].w -= aug[e]; f[id^1].w += aug[e]; aug[v] -= aug[e]; /// 修改可增广量,以后会用到 if (f[id].w == 0) u = v; /// 不回退到源点,仅回退到容量为0的弧的弧尾 } } for (id = cur[u]; id != -1; id = f[id].next)/// 从当前弧开始查找允许弧 { v = f[id].v; if (f[id].w > 0 && dis[u] == dis[v] + 1) /// 找到允许弧 { flag = true; pre[v] = u; cur[u] = id; aug[v] = min(aug[u], f[id].w); u = v; break; } } if (flag == false) { if (--gap[dis[u]] == 0) break; ///gap优化,层次树出现断层则结束算法 mindis = n; cur[u] = head[u]; for (id = head[u]; id != -1; id = f[id].next) { v = f[id].v; if (f[id].w > 0 && dis[v] < mindis) { mindis = dis[v]; cur[u] = id; /// 修改标号的同时修改当前弧 } } dis[u] = mindis + 1; gap[dis[u]]++; if (u != s) u = pre[u]; /// 回溯继续寻找允许弧 } } return max_flow; } void build(int mid) { int a, b; cnt = 0; memset(head, -1, sizeof(head)); S = 0; T = 3*n+1; for(int i = 1 ; i <= n; i++) { add(S, i, mid); add(i+2*n, T, mid); add(i, i+n, k); } memset(vis, 0, sizeof(vis)); for(int i = 0; i < m; i++) { a = pp[i].l; b = pp[i].r; vis[Find(a)][b] = 1; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(!vis[Find(i)][j]) add(i+n, j+2*n, 1); else add(i, j+2*n, 1); } } } int main() { int K; cin >>K; while(K--) { int p; scanf("%d %d %d %d",&n, &m, &k, &p); init(); for(int i = 0; i < m; i++) scanf("%d %d",&pp[i].l, &pp[i].r); int x, y; while(p--) { scanf("%d %d",&x, &y); Union(x, y); } int l = 0; int r = n; int ans = 0; int mid; while(l <= r) { mid = (l+r)>>1; build(mid); if(SAP(0, 3*n+1, 3*n+2) >= n*mid) { l = mid+1; ans = mid; continue; } r = mid-1; } printf("%d\n",ans); } return 0; }
HDU 3277 Marriage Match III(拆点+二分+最大流SAP),布布扣,bubuko.com
HDU 3277 Marriage Match III(拆点+二分+最大流SAP)
原文:http://blog.csdn.net/xu12110501127/article/details/38580329