zoukankan      html  css  js  c++  java
  • 题解 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;
        }
    
  • 相关阅读:
    欧拉函数的一个性质及其证明
    【机器人M号】题解
    【求和】题解
    uva11292贪心基础题目
    hdu 1009 贪心基础题
    近期计划,理清思路,大步向前
    hdu1712 分组背包
    TOJ3596 二维背包
    hdu1114 完全背包
    BestCoder Round #81 (div.2)1001
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9285506.html
Copyright © 2011-2022 走看看