牛客练习赛30D(消消乐)

链接:https://ac.nowcoder.com/acm/contest/216/D
思路:一道比较经典的网络流题目,按行和列建图,对于每一个a[i][j]='*'的点,我们从i向j拉一条边,那么原问题可以转换为,在行和列对应的二分图中,每一条边至少要有一个端点被选中,求最小点覆盖。由定理可知,最小点覆盖的值定于最大匹配数,如果用网络流的话也就是最大流。这个题难点在于方案输出,我们对于两种算法的方案输出解释一下:

网络流
代码:

#include<bits/stdc++.h>
using namespace std;

int n,m;
const int maxn = 6000;
const int INF = 1e9;

struct edge{
    int from,to,cap,flow;
};

struct Dinic{
    int n,m,s,t;
    vector<edge> edges;
    vector<int> G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];

    void init(int n){
        this->n = n;
        edges.clear();
        for(int i=0;i<=n;i++)G[i].clear();  
    }

    void addedge(int from,int to,int cap){
        edges.push_back(edge{from,to,cap,0});
        edges.push_back(edge{to,from,0,0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool bfs(){
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while(!q.empty()){
            int x = q.front();
            q.pop();
            for(int i=0;i<G[x].size();i++){
                edge &e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int dfs(int x,int a){
        if(x==t||a==0)return a;
        int flow = 0,f;
        for(int &i = cur[x];i<G[x].size();i++){
            edge &e = edges[G[x][i]];
            if(d[x] + 1 == d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow -=f;
                flow+=f;
                a-=f;
                if(a==0){
                    break;
                }
            }
        }
        return flow;
    }

    int maxflow(int s,int t){
        this->s = s;
        this->t = t;
        int flow = 0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,INF);
        }
        return flow;
    }
}solver;

int iscut[maxn];

void dfs(int u){
    iscut[u] = 1;
    for(int i=0;i<solver.G[u].size();i++){
        edge e = solver.edges[solver.G[u][i]];
        if(!iscut[e.to]&&e.cap>e.flow)dfs(e.to);//注意网络流中的写法
    }
}

char ch[2010];

int main(){
    scanf("%d%d",&n,&m);
    solver.init(n+m+2);
    for(int i=1;i<=n;i++){
        solver.addedge(0,i,1);
        scanf("%s",ch);
        for(int j=0;j<m;j++){
            if(ch[j]=='*')solver.addedge(i,j+1+n,1e9);
        }
    }
    for(int i=1;i<=m;i++)solver.addedge(n+i,n+m+1,1);
    int res = solver.maxflow(0,n+m+1);
    dfs(0);
    printf("%d\n",res);
    int l = 0,r = 0;
    for(int i=1;i<=n;i++){
        if(!iscut[i])l++;
    }
    for(int i=n+1;i<=n+m;i++){
        if(iscut[i])r++;
    }
    printf("%d ",l);
    for(int i=1;i<=n;i++){
        if(!iscut[i])printf("%d ",i);
    }
    printf("\n%d ",r);
    for(int i=n+1;i<=n+m;i++){
        if(iscut[i])printf("%d ",i-n);
    }
    printf("\n");
    return 0;
}

匈牙利算法

#include<bits/stdc++.h>
using namespace std;

const int maxn = 4010;

//注意编号从1开始
vector<int> G[maxn];
bool vis[maxn];
int link[maxn];
int m;
int nx,ny;

    void init(int nx,int ny){
        for(int i=0;i<=nx+ny;i++)G[i].clear();
    }

    inline void addedge(int from,int to){
        G[from].push_back(to);
    }

    bool dfs(int u){
        vis[u] = 1;
        for(int i=0;i<G[u].size();i++){
            int v = G[u][i];
            if(!vis[v]){
                vis[v] = 1;
                if(link[v]==-1||dfs(link[v])){
                    link[v] = u;
                    link[u] = v;
                    return true;
                }
            }
        }
        return false;
    }

    int hungrey(){
        int res = 0;
        memset(link,-1,sizeof(link));
        for(int i=1;i<=nx;i++){
            memset(vis,0,sizeof(vis));
            if(dfs(i))res++;
        }
        return res;
    }

char a[maxn][maxn];

int main(){
    scanf("%d%d",&nx,&ny);
    init(nx,ny);
    for(int i=1;i<=nx;i++){
        scanf("%s",a[i]+1);
        for(int j=1;j<=ny;j++){
            if(a[i][j]=='*')addedge(i,j+nx);
        }
    }
    int res = hungrey();
    printf("%d\n",res);
    memset(vis,0,sizeof(vis));//记得先清空已经用过的标记
    for(int i=1;i<=nx;i++){
        if(link[i]==-1)dfs(i);
    }
    int ans = 0;
    for(int i=1;i<=nx;i++){
        if(!vis[i])ans++;
    }
    printf("%d ",ans);
    for(int i=1;i<=nx;i++){
        if(!vis[i])printf("%d ",i);
    }
    ans = 0;
    for(int i=nx+1;i<=nx+ny;i++){
        if(vis[i])ans++;
    }
    printf("\n%d ",ans);
    for(int i=nx+1;i<=nx+ny;i++){
        if(vis[i])printf("%d ",i-nx);
    }
    printf("\n");
    return 0;
}

关于求解最小点覆盖集,可以参考如下:
https://blog.csdn.net/niushuai666/article/details/7036897
思路是从一个未匹配的点开始,沿着未匹配的边走,沿途标记所有的点,对于左侧的未标记的点、右侧标记的点就是选中的最小点覆盖集。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 还记得那天,你突然被引爆的泪腺,眼泪哗哗地往下掉,三十而立的人,在母亲面前,仅絮叨了几句,几个月来的压力,瞬间释放...
    科姬兔阅读 549评论 2 0
  • 神话是以故事的形式表达远古人民对自然、社会现象的认识和愿望,是“通过人民的幻想用一种不自觉的艺术形式加工过的自然和...
    真缜阅读 2,098评论 0 4
  • 从学校搬出来住已经两个多月了,小区安静便利,距离公司10分钟左右的步行路程,大门口外有一个小菜市场,每天下...
    远山一梦阅读 223评论 0 1