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