题解 P1682 【过家家】

P1682 过家家

题目描述
有2n个小学生来玩过家家游戏,其中有n个男生,编号为1到n,另外n个女生,编号也是1到n.每一个女生可以先选择一个和她不吵嘴的男生来玩,除此之外,如果编号为X的女生的朋友(也是女生,且编号为Y)不和编号为Z的男生吵嘴,那么X也可以选择Z.此外,朋友关系是可以传递的,比如a和b是朋友,b和c是朋友,那么我们可以认为a和c也是朋友.

当每一位女生都选择了玩伴,那么他们会开始新一轮游戏.在每一轮后,每个女生都会开始去找一个新的男生做玩伴(以前没选过).而且每一个女生最多能强制k个男生接受,无论他们以前是否吵嘴.

现在你的任务就是确定这2n个小学生最多能玩几轮游戏.

输入输出格式
输入格式:
第一行有4个整数n,m,k,f(3<=n<=250,0<m<n*n,0<=f<n).

n表示有2n个小学生,其中n个男生n个女生.

接下来m行,每行包含2个数字a,b表示编号为a的女生和编号为b的男生从没吵嘴过.

再接下来f行,每行包含2个数字c,d表示编号为c的女生和编号为d的女生是朋友.

输出格式:
对于每组数据,输出一个整数,表示2n个小学生最多能玩几轮.


这一题和P3153 [CQOI2009]跳舞 几乎是一模一样,那篇题解可以看这里,建图 + 最大流 + 二分即可得到答案,这里不在赘述,写这篇题解是要解决题目中连通性的问题

题目中有提到过,朋友关系是可以传递的,比如a和b是朋友,b和c是朋友,那么我们可以认为a和c也是朋友,据此我们有两个思路:

1.并查集

2.Floyd

并查集是否可行

虽然我很擅长并查集,但是很可惜,并查集的做法在这题并不是特别适用:理由如下:

并查集的最大用处是,利用父亲这一概念很方便的判断两元素是否处于同一集合,但通过某一节点,我们不能很快确定其他节点是否与他在一集合内,唯一想到的方法是遍历所有节点,一一判断是否属于同一集合,比较麻烦,故不适用并查集。

Floyd是否可行

Floyd算法可以判断图的联通性,伪代码大概如下:

for(所有节点(作为中间点)){
	for(所有节点(作为起点)){
    	for(所有节点(作为终点)){
        	if(起点与中间点联通 && 中间点与终点联通)
            起点与终点联通
        	}
    	}
	}

这就是传递闭包,能适用与这题,理由如下:

利用传递闭包,我们在处理完关系之后可以得到一个二维数组,可以很容易的判断两点是否联通

所以我们使用Floyd传递闭包,解决关系问题,不过要对上述伪代码做适当修改:(男孩B和女孩A玩 && 女孩A和女孩C是朋友)---> 男孩和女孩C玩

所以我们得到代码:

void Floyd(){
    for(int k = num + 1;k <= 2 * num;k++){//所有女孩
        for(int i = 1;i <= num;i++){//所有男孩
            for(int j = num + 1;j <= 2 * num;j++){//所有女孩
                dance[i][j] |= dance[i][k] && dance[k][j];//男孩选女孩(建图是也是先男孩再连女孩)
                }
            }
        }
    }
    
    
//主函数部分
    for(int i = 1;i <= nr;i++){
        u = RD();v = RD();
        dance[v][u + num] = 1;//女孩编号 + num
        }
    for(int i = 1;i <= nf;i++){
        u = RD();v = RD();
        dance[u + num][v + num] = dance[v + num][u + num] = 1;
        }
    Floyd();

最后二分多次建图跑最大流验证答案即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 100019,INF = 1e9;
int num,k,nr,nf,nume = 1;
int dance[190][190];
int frind[190][190];
int s,t,maxflow;
int head[maxn << 2];
struct Node{
    int v,dis,nxt;
    }E[maxn << 3];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
int d[maxn];
bool bfs(){
    queue<int>Q;
    memset(d,0,sizeof(d));
    d[s] = 1;
    Q.push(s);
    while(!Q.empty()){
        int u = Q.front();Q.pop();
        for(int i = head[u];i;i = E[i].nxt){
            int v = E[i].v;
            if(E[i].dis && !d[v]){
                d[v] = d[u] + 1;
                if(v == t)return 1;
                Q.push(v);
                }
            }
        }
    return 0;
    }
int Dinic(int u,int flow){
    if(u == t)return flow;
    int rest = flow,k;
    for(int i = head[u];i;i = E[i].nxt){
        int v = E[i].v;
        if(d[v] == d[u] + 1 && rest && E[i].dis){
            k = Dinic(v,min(rest,E[i].dis));
            if(!k)d[v] = 0;
            E[i].dis -= k;
            E[i ^ 1].dis += k;
            rest -= k;
            }
        }
    return flow - rest;
    }
void build(int a){
    memset(head,0,sizeof(head));
    nume = 1;
    for(int i = 1;i <= num;i++){
        add(s,i,a);
        add(i,s,0);//源点到每个♂
        add(i + num,i + 2 * num,k);
        add(i + 2 * num,i + num,0);//♀分两部
        add(i + 2 * num,t,a);
        add(t,i + 2 * num,0);//女到汇点
        }
    for(int i = 1;i <= num;i++){
        for(int j = num + 1;j <= 2 * num;j++){
            if(dance[i][j]){
                add(i,j + num,1);
                add(j + num,i,0);
                }
            else{
                add(i,j,1);
                add(j,i,0);
                }
            }
        }
    }
bool check(int mid){
    build(mid);
    maxflow = 0;
    int flow = 0;
    while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
    if(maxflow == mid * num)return 1;
    return 0;
    }
int search(int l,int r){
    int ans;
    while(l <= r){
        int mid = l + r >> 1;
        if(check(mid))l = mid + 1,ans = mid;
        else r = mid - 1;
        }
    return ans;
    }
void Floyd(){
    for(int k = num + 1;k <= 2 * num;k++){
        for(int i = 1;i <= num;i++){
            for(int j = num + 1;j <= 2 * num;j++){
                dance[i][j] |= dance[i][k] && dance[k][j];
                }
            }
        }
    }
int main(){
    num = RD();nr = RD();k = RD();nf = RD();
    s = num * 4 + 19;t = s + 1;
    int u,v;
    for(int i = 1;i <= nr;i++){
        u = RD();v = RD();
        dance[v][u + num] = 1;
        }
    for(int i = 1;i <= nf;i++){
        u = RD();v = RD();
        dance[u + num][v + num] = dance[v + num][u + num] = 1;
        }
    Floyd();
    printf("%d
",search(0,num + k));
    return 0;
    }
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9285506.html