邓俊辉算法训练营习题 象棋 (二分图匈牙利算法 )

有一个由1和0组成的正方形矩阵如图

只有值为1的位置才能存放某个物品,同时每一行每一列只能存放一个物品求出最多能存放的物品数,要求最多能存放物品的数量?

一、将数值等于1的点存储到二分图当中

1-4代表着1 - 4行  ----  5 - 8代表着1 - 4列

1与6的连线代表着第一行第(6 - 4)= 2列的值是1

二、用邻接表存储二分图

  建立邻接表

struct node
{   
    //next:下一条邻接边
    //to:本条边所指向的终点
    int next,to;
}e[M];

 将二分图的内容加入到邻接表当中(博主脑子不太够用只能用这种笨办法来分析代码)

  

//邻接表连边,表示连一条x到y的有向边
//x:起点
//y:终点
void add(int x,int y){
    ++cnt;
    e[cnt].next = ihead[x];//下一个邻接点 就是之前的头
    e[cnt].to = y;
    ihead[x] = cnt;//邻接链的头接点
}

三、匈牙利算法查找增广路

1 和 2点一开始都在 mc[y] = 0的条件下找到了自己的匹配点

3的匹配点6已将被1占用 通过dfs(mc[y])递归找到1存在增广路8,则1与8重新匹配,3与6匹配(代码由于邻接表存储的关系并不是1与6先匹配,但不影响算法思想)

上图也是与实际代码不符合,因为邻接表的建立是前插入的,图为后插入的解决方法

//mc:表示每个点所匹配到的另一个点match
//vis:Y集元素是否被访问过
int mc[N];
bool vis[N];
//匈牙利算法
//x:x集上的点,从当前点出发找增广路
//返回值:若找到增广路则返回true,否则返回false
bool dfs(int x){
    //遍历邻接表
    for(int i = ihead[x]; i != 0; i = e[i].next){
        int y = e[i].to;
        if(!vis[y]){//如果找到一个Y集上的点没有标记
            vis[y] = true;//标记该点
            //如果y是没有匹配点的,说明找到了一条增广路;或者说递归查找y的匹配点,得到了一条增广路
            if(mc[y] == 0 || dfs(mc[y])){
                //找到了增广路,更新mc数组
                mc[x] = y;
                mc[y] = x;
                return true;
            }
        }
    }
    return false;
}

 

-----------------习题-------------------

多的象棋“车”,在一个n×n的棋盘上你能放多少个“车”呢?注意,所给棋盘上有些位置不能放任何东西。同时,某一行(列)最多只能存在一个“车”。

输入
第一行为一个正整数n。

接下来n行,每行包含n个整数,若为0表示这个位置不能放“车”;若为1表示这个位置可以放“车”。

输出
输出一个整数,表示最多能放多少个“车”。

样例1输入
5
1 0 0 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 0
0 0 0 1 0
样例1输出
3
样例1解释
我们这样放就只能放2个“车”:

车 0 0 0 0
0 0 0 0 0
0 0 0 1 0
1 0 0 车 0
0 0 0 1 0
若我们这样放就能放下3个了:

车 0 0 0 0
0 0 0 0 0
0 0 0 1 0
1 车 0 1 0
0 0 0 车 0

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 505*2, M= N * N;
struct node
{   
    //next:下一条邻接边
    //to:本条边所指向的终点
    int next,to;
}e[M];
//ihead:邻接表的头
//cnt:邻接表大小
//mc:表示每个点所匹配到的另一个点match
//vis:Y集元素是否被访问过
int cnt,ihead[N],mc[N];
bool vis[N];
//邻接表连边,表示连一条x到y的有向边
//x:起点
//y:终点
void add(int x,int y){
    ++cnt;
    e[cnt].next = ihead[x];//下一个邻接点 就是之前的头
    e[cnt].to = y;
    ihead[x] = cnt;//邻接链的头接点
}
//匈牙利算法
//x:x集上的点,从当前点出发找增广路
//返回值:若找到增广路则返回true,否则返回false
bool dfs(int x){
    //遍历邻接表
    for(int i = ihead[x]; i != 0; i = e[i].next){
        int y = e[i].to;
        if(!vis[y]){//如果找到一个Y集上的点没有标记
            vis[y] = true;//标记该点
            //如果y是没有匹配点的,说明找到了一条增广路;或者说递归查找y的匹配点,得到了一条增广路
            if(mc[y] == 0 || dfs(mc[y])){
                //找到了增广路,更新mc数组
                mc[x] = y;
                mc[y] = x;
                return true;
            }
        }
    }
    return false;
}
int getAnswer(int n,vector<vector<int>> e){
    cnt = 0;
    for(int i = 0;i < n;i++){
        ihead[i] = 0;
        mc[i] = 0;
    }
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            if(e[i - 1][j - 1] == 1)
                add(i,j + n);
    int ans = 0;
    for(int i = 1; i <= n; ++i)
      //当mc[i] = 0时
      if(!mc[i]){
        //如果x集中的第i个点没有匹配到Y集上的点,则从这个点出发寻找增广路
        memset(vis, 0, sizeof(bool) * (n * 2 + 1));
        //将数组置为0
        if(dfs(i))
            ++ans;//如果找到,答案直接+1
      }
    return ans;
}
int main(int argc, char const *argv[]) {
    // n - 行列数
    int n;
    scanf("%d",&n);

    vector<vector<int>> e;
    for(int i = 0;i < n;i++){
        vector<int> q;
        for(int j = 0;j < n;j++){
            int a;
            scanf("%d",&a);
            q.push_back(a);
        }
        e.push_back(q);
    }
    cout << getAnswer(n,e);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值