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