二分图最大匹配

HDU 1150:

http://acm.hdu.edu.cn/showproblem.php?pid=1150

  最小覆盖点 == 最大匹配数(选取最少的点数,使这些点和所有的边都有关联——把所有的边的覆盖)

      两台机器,有n和m个工作模式,起始工作模式都为0,现在有k件工作,第i件工作可分别在两个机器上用各自的模式工作,但换模式要重启,问重启的最小次数。

      写的时候因为是找二分最大匹配的题目时找到写的,想到了二分上去,也知道是求最小覆盖点 == 最大匹配数,但不是很能理解,先把代码写了再说。

      写的时候注意起始模式是0,所以换模式时把0的排除再外。(因为这个原因错了很多次)

一:邻接阵做法

代码
#include<iostream>
using namespace std;

int n,m,k;
int map[105][105]; //记录X,Y对应点可否连接
int vis[105]; //每次找增广路时对Y中点是否访问
int dir[105]; //Y中点匹配的X中点的位置

int find(int a)
{
int i;
for(i=0;i<m;i++)
{
if(map[a][i]==1 && vis[i] == 0)
{
vis[i]
= 1;
if(dir[i] == -1 || find(dir[i]))
{
dir[i]
= a;
return 1;
}
}
}
return 0;
}

int main()
{
int i,x,y;
while(scanf("%d",&n)!=EOF && n!=0)
{
memset(map,
0,sizeof(map));
memset(dir,
-1,sizeof(dir));
scanf(
"%d%d",&m,&k);
while(k--)
{
scanf(
"%d%d%d",&i,&x,&y);
if(x && y)
map[x][y]
= 1; //应该不能再用map[y][x] = 1
}
int sum = 0;
for(i=0;i<n;i++)
{
memset(vis,
0,sizeof(vis));
if(find(i))
sum
++;
}

printf(
"%d\n",sum);
}
return 0;
}

二:动态邻接表做法.

代码
#include<iostream>
using namespace std;

int n,m,k;
int vis[105]; //每次找增广路时对Y中点是否访问
int dir[105]; //Y中点匹配的X中点的位置

struct edge
{
int from;
int to;
edge
* next;
edge()
{
from
= to = 0;
next
= NULL;
}
};
edge
* List[105];

void add_edge(int f,int t)
{
edge
* node = new edge();
node
->from = f;
node
->to = t;
node
->next = List[f];
List[f]
= node;
}

int find(edge* node)
{
while(1)
{
if(node == NULL)
{
break;
}
int i = node->to;
if(vis[i] == 0)
{
vis[i]
= 1;
if(dir[i] == -1 || find(List[dir[i]]))
{
dir[i]
= node->from;
return 1;
}
}
node
= node->next;
}
return 0;
}

int main()
{
int i,x,y;
while(scanf("%d",&n)!=EOF && n!=0)
{
for(i=0;i<=n;i++)//链表清空,一开始没清空,错了很多次
{
List[i]
= NULL;
}
memset(dir,
-1,sizeof(dir));
scanf(
"%d%d",&m,&k);
while(k--)
{
scanf(
"%d%d%d",&i,&x,&y);
if(x && y)
add_edge(x,y);
}
int sum = 0;
for(i=1;i<=n;i++)
{
memset(vis,
0,sizeof(vis));
if(find(List[i]))
sum
++;
}
printf(
"%d\n",sum);
}
return 0;
}

HDU 1151:

http://acm.hdu.edu.cn/showproblem.php?pid=1151

     最小路径覆盖(选取最少的边覆盖所有的点) == 节点数 - 最大匹配数


代码
#include <iostream>
using namespace std;
#define MAXN 125

int vis[MAXN];
int link[MAXN];
int n,m;

struct edge
{
int from;
int to;
edge
* next;
edge()
{
from
= to = 0;
next
= NULL;
}
};
edge
* List[MAXN];

void add_edge(int f,int t)
{
edge
* node = new edge();
node
->from = f;
node
->to = t;
node
->next = List[f];
List[f]
= node;
}

bool find(edge* node)
{
while(1)
{
if(node == NULL)
break;
int i = node->to;
if(vis[i] == 0)
{
vis[i]
= 1;
if(link[i] == 0 || find(List[link[i]]))
{
link[i]
= node->from;
return 1;
}
}
node
= node->next;
}

return 0;
}

int main()
{
int i;
int t,x,y;
scanf(
"%d",&t);
while(t--)
{
memset(link,
0,sizeof(link));
scanf(
"%d%d",&n,&m);
for(i=0;i<=n;i++)
{
List[i]
= NULL;
}
for(i=0;i<m;i++)
{
scanf(
"%d%d",&x,&y);
add_edge(x,y);
}
int sum = 0;
for(i=1;i<=n;i++)
{
memset(vis,
0,sizeof(vis));
if(find(List[i]))
sum
++;
}

printf(
"%d\n",n-sum);
}
return 0;
}

 

HDU 1068:

http://acm.hdu.edu.cn/showproblem.php?pid=1068

  二分图最大独立集=顶点数-二分图最大匹配

代码
#include <iostream>
using namespace std;

const long max_edge = 100005;
const long max_point = 1005;
int n;
int Index;

struct node
{
int y;
int next;
}stu[max_edge];
int pre[max_point];//以该点为起点的第一条在stu中的存储位置
int vis[max_point];//Y中结点的访问情况
int link[max_point];//Y中与X中配对的情况

void add_edge(int a,int b)
{
stu[Index].y
= b;
stu[Index].next
= pre[a];
pre[a]
= Index++;
}

void Init()
{
int i,j,s,t,m;
char c;

Index
= 1;
memset(pre,
-1,sizeof(pre));
memset(link,
-1,sizeof(link));

for(i=0;i<n;i++)
{
cin
>>s>>c>>c>>m>>c;
for(j=0;j<m;j++)
{
scanf(
"%d",&t);
add_edge(s,t);
}
}
}

bool find(int dir)
{
int i;
for(i=pre[dir]; i!=-1; i=stu[i].next)
{
int ii = stu[i].y;
if(vis[ii] == 0)
{
vis[ii]
= 1;
if(link[ii] == -1 || find(link[ii]))
{
link[ii]
= dir;
return true;
}
}
}

return false;
}

void Play()
{
int i;
int ans = 0;
for(i=0;i<n;i++)
{
memset(vis,
0,sizeof(vis));
if(find(i))
ans
++;
}

printf(
"%d\n",n-ans/2);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Init();
Play();
}
return 0;
}

HDU 1281:

http://acm.hdu.edu.cn/showproblem.php?pid=1281

写的时候写搓了,一个小错误,find1递归的时候用了find函数,很是郁闷!

当数据量小的时候可以不用静态连接表存储,毕竟连接阵用起来比连接表方便!

代码
#include <iostream>
using namespace std;

const long max_point = 1005;
const long max_edge = 100005;

int n,m,k;
int cas = 0;
int mat[max_point][max_point];
int vis[max_point];
int linky[max_point];
int linkx[max_point];

void Init()
{
int i;
int a,b;
memset(mat,
0,sizeof(mat));

for(i=0;i<k;i++)
{
scanf(
"%d%d",&a,&b);
mat[a][b]
= 1;
}
}

bool find(int dir,bool flag)
{
int i;
for(i=1;i<=m;i++)
{
if(mat[dir][i] && !vis[i])
{
vis[i]
= 1;
if(linky[i]==-1 || find(linky[i],flag))
{
if(flag)
{
linkx[dir]
= i;
linky[i]
= dir;
}
return true;
}
}
}

return false;
}

bool can()
{
int i;
for(i=1;i<=n;i++)
{
if(linkx[i] == -1)
{
memset(vis,
0,sizeof(vis));
if(find(i,0))
{
return true;
}
}
}

return false;
}
void Play()
{
int i,j;

int ans = 0;
memset(linkx,
-1,sizeof(linkx));
memset(linky,
-1,sizeof(linky));
for(i=1;i<=n;i++)
{
memset(vis,
0,sizeof(vis));
if(find(i,1))
ans
++;
}

int ans1 = 0;
for(i=1;i<=n;i++)
{
if(linkx[i] != -1)
{
int tmp = linkx[i];
linkx[i]
= -1;
linky[tmp]
= -1;
mat[i][tmp]
= 0;

if(!can())
ans1
++;

mat[i][tmp]
= 1;
linky[tmp]
= i;
linkx[i]
= tmp;
}
}

printf(
"Board %d have %d important blanks for %d chessmen.\n",++cas,ans1,ans);
}

int main()
{
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
Init();
Play();
}
return 0;
}

HDU 1498:

http://acm.hdu.edu.cn/showproblem.php?pid=1498

感觉来了,这题没花什么精力,一杯咖啡的时候想了出来。对每个颜色,求最小点覆盖,如果大于k,则不能在k时间内完成,输出;否则可以完成。

代码
#include <iostream>
using namespace std;

int n,k;
const long max_color = 55;
const long maxn = 105;
int Hash[max_color];
int map[max_color][maxn][maxn];
int vis[maxn];
int linkx[maxn],linky[maxn];

void Init()
{
int i,j;

memset(map,
0,sizeof(map));
memset(Hash,
0,sizeof(Hash));

for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
int color;
scanf(
"%d",&color);
map[color][i][j]
= 1;
Hash[color]
= 1;
}
}
}

bool find(int dir,int u)
{
int i,j;
for(i=0;i<n;i++)
{
if(1 == map[dir][u][i])
{
if(0 == vis[i])
{
vis[i]
= 1;
if(linky[i] == -1 || find(dir,linky[i]))
{
linky[i]
= u;
linkx[u]
= i;
return true;
}
}
}
}

return false;
}
int MaxMatch(int dir)
{
int i;
int sum = 0;
memset(linkx,
-1,sizeof(linkx));
memset(linky,
-1,sizeof(linky));

for(i=0;i<n;i++)
{
if(-1 == linkx[i])
{
memset(vis,
0,sizeof(vis));
if(find(dir,i))
{
sum
++;
}
}
}

return sum;
}

void Play()
{
int i;
int flag = 0;
for(i=1;i<=50;i++)
{
//对应每种颜色
if(1 == Hash[i])
{
int num = MaxMatch(i);

/*if(flag != 0)
{//这个放在外面,PE了一次
printf(" ");
}
*/

if(num > k)
{
if(flag != 0)
{
printf(
" ");
}
printf(
"%d",i);
flag
= 1;
}
}
}

if(0 == flag)
{
printf(
"-1");
}

printf(
"\n");
}

int main()
{
while(scanf("%d%d",&n,&k)!=EOF && (n!=0 || k!=0))
{
Init();
Play();
}
return 0;
}

HDU 1528:

http://acm.hdu.edu.cn/showproblem.php?pid=1528

简单题,卡片为点,大小关系为边,求最大匹配。 

代码
#include <iostream>
#include
<map>
using namespace std;
const long maxn = 30;

int k;
map
<char,int> M;
int num_Adam[maxn],num_Eve[maxn];
int linkx[maxn],linky[maxn];
int mat[maxn][maxn],vis[maxn],pre[maxn];

void Init()
{
int i,j;
char c1,c2;
memset(mat,
0,sizeof(mat));

M[
'2'] = 0;M['3'] = 1;M['4'] = 2;M['5'] = 3;M['6'] = 4;
M[
'7'] = 5;M['8'] = 6;M['9'] = 7;M['T'] = 8;M['J'] = 9;
M[
'Q'] = 10;M['K'] = 11;M['A'] = 12;
M[
'H'] = 3;M['S'] = 2;M['D'] = 1;M['C'] = 0;


scanf(
"%d",&k);
for(i=0;i<k;i++)
{
cin
>>c1>>c2;
num_Adam[i]
= M[c1] * 4 + M[c2];
}
for(i=0;i<k;i++)
{
cin
>>c1>>c2;
num_Eve[i]
= M[c1] * 4 + M[c2];
}

for(i=0;i<k;i++)
{
for(j=0;j<k;j++)
{
if(num_Adam[i] < num_Eve[j])
{
mat[i][j]
= 1;
}
}
}
}

bool find(int dir)
{
int i;
for(i=0;i<k;i++)
{
if(1 == mat[dir][i] && 0 == vis[i])
{
vis[i]
= 1;
if(linky[i] == -1 || find(linky[i]))
{
linky[i]
= dir;
linkx[dir]
= i;
return true;
}
}
}

return false;
}

void Play()
{
int i;
int ans = 0;
memset(linkx,
-1,sizeof(linkx));
memset(linky,
-1,sizeof(linky));

for(i=0;i<k;i++)
{
if(-1 == linkx[i])
{
memset(vis,
0,sizeof(vis));
if(find(i))
ans
++;
}
}

printf(
"%d\n",ans);
}

int main()
{
int t;
scanf(
"%d",&t);
while(t--)
{
Init();
Play();
}
return 0;
}
原文地址:https://www.cnblogs.com/silencExplode/p/1896036.html