有一个由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;
}