zoukankan      html  css  js  c++  java
  • [BZOJ 1305] 跳舞

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1305

    Solution:

    发现res是否可行具有单调性,二分答案

    容易看出每次check(mid)用网络流判断,关键在于建图:

    1)将每一个人拆成两个点,男孩的两个点为X1,X2,女孩为Y1,Y2

    2)将相互喜欢的将X1,Y1相连,互相讨厌的将X2,Y2相连,容量为1

    3)每一个X1向X2连一条容量为k的边,每一个Y2向Y1连一条容量为k的边

    4)从源点向每一个X1连容量mid的边,从每一个Y1向汇点连容量为mid的边

     

    这里建图的难点还是在拆点上,

    拆点的目的在于将从该点出发的流量分类,并能对每一类进行容量限制

    EX:为了使向“讨厌的”流量不超过k,我们将Xi流向“讨厌的”节点的流量专门设一个源点(分类)

    这样将 Xi1  ---->   Xi2 的容量设为k即可满足这一条件(加以容量限制)

     

    于是当遇到每个点根据不同流向的流量要加以不同限制时,

    考虑将点拆成好几个,再对每个点建一个总源点(此题没有必要,只有2类)

     

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=200+10;
    const int INF=1<<27;
    
    struct edge
    {
        int to,cap,rev;
    };
    vector<edge> G[MAXN];
    
    char dat[MAXN][MAXN];
    int n,k,S,T,level[MAXN],iter[MAXN];
    
    void add_edge(int from,int to,int cap)
    {
        G[from].push_back(edge{to,cap,G[to].size()});
        G[to].push_back(edge{from,0,G[from].size()-1});
    }
    
    bool bfs()
    {
        memset(level,-1,sizeof(level));
        queue<int> que;que.push(S);level[S]=0;
        
        while(!que.empty())
        {
            int u=que.front();que.pop();
            for(int i=0;i<G[u].size();i++)
            {
                edge v=G[u][i];
                if(v.cap && level[v.to]==-1)
                    level[v.to]=level[u]+1,que.push(v.to);
            }
        }
        return level[T]!=-1;
    }
    
    int dfs(int v,int f)
    {
        if(v==T) return f;
        int ret=0;
        for(int& i=iter[v];i<G[v].size();i++)
        {
            edge &e=G[v][i];
            if(e.cap && level[e.to]==level[v]+1)
            {
                int d=dfs(e.to,min(f,e.cap));
                e.cap-=d;G[e.to][e.rev].cap+=d;ret+=d;f-=d;  //一定要加f-=d
                if(!f) break;  //这里的剪枝很重要
            }
        }
        return ret;
    }
    
    int dinic()
    {
        int ret=0;
        while(bfs())
            memset(iter,0,sizeof(iter)),ret+=dfs(S,INF);
        return ret;
    }
    
    void build(int flow)
    {
        for(int i=0;i<MAXN;i++) G[i].clear();
        for(int i=1;i<=n;i++) add_edge(S,i,flow),add_edge(i,i+2*n,k);
        for(int i=n+1;i<=2*n;i++) add_edge(i,T,flow),add_edge(i+2*n,i,k);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dat[i][j]=='Y') add_edge(i,n+j,1);
                else add_edge(i+2*n,n+j+2*n,1);
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        S=0;T=4*n+1;
        for(int i=1;i<=n;i++) scanf("%s",dat[i]+1);
                
        int l=0,r=50;  //从0开始二分
        while(l<=r)
        {
            int mid=(l+r)>>1;
            build(mid);
            if(dinic()<n*mid) r=mid-1;
            else l=mid+1;
        }
        printf("%d",r);
        return 0;
    }

    Review:

    关于Dinic的两种模板:

    (1)找到一条路径便立即返回

    (2)对于每个点枚举完所有情况后一起返回

    由于Dinic能加上当前弧优化,所以(1)虽然开栈次数看上去多了,但效率绝对不比(2)差

    而(2)如果不加上容量已为0时的剪枝,则要比(1)慢得多

    注意:使用(2)时每次一定要f-=d

  • 相关阅读:
    unity_动画_状态机
    有内涵的技术论坛
    ios开发——常用经典算法OC篇&冒泡/快速
    iOS开发——C篇&文件操作
    iOS开发——C篇&预处理
    iOS开发——C篇&结构体与枚举
    iOS开发——C篇&函数解析
    iOS开发——C篇&动态内存分析
    UML第二次作业(代码互评)
    我与UML相爱相杀的狗血日常
  • 原文地址:https://www.cnblogs.com/newera/p/9096391.html
Copyright © 2011-2022 走看看