HDU 3277 Marriage Match III(拆点+二分+最大流SAP)

来源:互联网 发布:washington post 知乎 编辑:程序博客网 时间:2024/06/07 06:01

这个题目是说,有n个女的和男的找伴侣。然后女的具有主动选择权,每个女的可以选自己喜欢的男的,也可以挑选k个不喜欢的男的,做法就是:把女的拆点,u1->u2建立一条容量为k的边。如果遇见喜欢的男生i->j+2*n建一条容量为1的边,否则i+n->j+2*n建一条容量为1的边。最后将源点和女生相连容量为mid,汇点与男生相连容量为mid。枚举mid,看是否会产生满流。

可能姿势不够优美dinic超时了啊,换成SAP快了很多啊、、、

Marriage Match III

Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1405    Accepted Submission(s): 421


Problem Description
Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the ever game of play-house . What a happy time as so many friends play together. And it is normal that a fight or a quarrel breaks out, but we will still play together after that, because we are kids. 

Now, there are 2n kids, n boys numbered from 1 to n, and n girls numbered from 1 to n. As you know, ladies first. So, every girl can choose a boy first, with whom she has not quarreled, to make up a family. Besides, the girl X can also choose boy Z to be her boyfriend when her friend, girl Y has not quarreled with him. Furthermore, the friendship is mutual, which means a and c are friends provided that a and b are friends and b and c are friend. 

Once every girl finds their boyfriends they will start a new round of this game—marriage match. At the end of each round, every girl will start to find a new boyfriend, who she has not chosen before. So the game goes on and on. On the other hand, in order to play more times of marriage match, every girl can accept any K boys. If a girl chooses a boy, the boy must accept her unconditionally whether they had quarreled before or not. 

Now, here is the question for you, how many rounds can these 2n kids totally play this game?
 

Input
There are several test cases. First is an integer T, means the number of test cases. 
Each test case starts with three integer n, m, K and f in a line (3<=n<=250, 0<m<n*n, 0<=f<n). n means there are 2*n children, n girls(number from 1 to n) and n boys(number from 1 to n).
Then m lines follow. Each line contains two numbers a and b, means girl a and boy b had never quarreled with each other. 
Then f lines follow. Each line contains two numbers c and d, means girl c and girl d are good friends.
 

Output
For each case, output a number in one line. The maximal number of Marriage Match the children can play.
 

Sample Input
14 5 1 21 12 33 24 24 41 42 3
 

Sample Output
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;}


0 0