二分图最大匹配

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 ;
}

你可能感兴趣的:(二分图)