第一个例题:POJ3041
题目链接:Asteroids
题目的大概意思是:每一行、每一列都有一些障碍物,你每次可以扫清一行或者一列的障碍物,求你最少的操作数。
用二分图去解决,可以这样去想:将每一行当作二分图的左边节点,将每一列当作二分图的右节点,当一行与一列的交点有障碍物时,将两个的节点连成一条线。所以题目就变成了求覆盖所有的边(所有障碍物),需要的最少的节点的个数。也就是最小点覆盖问题,在二分图中,最小点覆盖即为最大匹配数。
代码如下:
/*************************************************************************
> File Name: main.cpp
> Author:Eagles
> Mail:None
> Created Time: 2018年09月14日 星期五 18时46分23秒
> Description:POJ3041,最小顶点覆盖
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define N 600
struct node
{
int to;
int nex;
}E[N*N];
int head[N];
int pre[N];
bool vis[N];
int n,cnt,k;
void addEdge(int a, int b)
{
E[cnt].to=b;
E[cnt].nex=head[a];
head[a]=cnt++;
}
void init()
{
memset(head,-1,sizeof(head));
memset(pre,-1,sizeof(pre));
cnt=0;
for (int i=0; i<k; i++)
{
int a,b;
scanf("%d%d",&a,&b);
addEdge(a-1,b-1);
}
}
int dfs(int x)
{
for (int i=head[x]; i!=-1; i=E[i].nex)
{
if (!vis[E[i].to])
{
vis[E[i].to]=true;
if (pre[E[i].to]==-1||dfs(pre[E[i].to]))
{
pre[E[i].to]=x;
return 1;
}
}
}
return 0;
}
int main()
{
while (~scanf("%d%d",&n,&k))
{
init();
int sum=0;
for (int i=0; i<n; i++)
{
memset(vis,false,sizeof(vis));
if (dfs(i))
sum++;
}
printf("%d\n",sum);
}
return 0;
}
第二个例题:POJ2226
题目链接:Muddy Fields
题目乍一看,跟上面差不多,然后我毫不犹豫地按照上面的思路做了,提交之后就wa了…
仔细对比题目,就会发现,上一题是一整行或一整列都可以扫清,而这一题只是扫清一行或一列中连续的部分,这就是二者的差别。第一个例题中,一行或一列可以代表整个一行或一列,而这道题一行中如果出现不连续的两段或者两段以上,那么需要的节点数就大于1,所以就不能单纯地用行号或列号去代表二分图左边或者右边的节点。所以这道题构图的时候,将连续的地方用一个节点代替,剩下的就跟第一题一样了。
详见代码:
/*************************************************************************
> File Name: main.cpp
> Author:Eagles
> Mail:None
> Created Time: 2018年09月14日 星期五 19时51分30秒
> Description:POJ2226
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define N 2000
struct node
{
int to;
int nex;
}E[N*N];
int head[N];
bool vis[N];
int pre[N];
int r,c,cnt;
char map[N][N];
int col[N][N],row[N][N];
int co,ro;
void addEdge(int a, int b)
{
E[cnt].to=b;
E[cnt].nex=head[a];
head[a]=cnt++;
}
void init()
{
memset(head,-1,sizeof(head));
memset(pre,-1,sizeof(pre));
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
cnt=ro=co=0;
for (int i=1; i<=r; i++)
{
for (int j=1; j<=c; j++)
{
cin>>map[i][j];
}
}
for (int i=1; i<=r; i++)
{
for (int j=1; j<=c; j++)
{
if (map[i][j]=='*')
{
if (row[i][j-1])
row[i][j]=row[i][j-1];
else
row[i][j]=++ro;
if (col[i-1][j])
col[i][j]=col[i-1][j];
else
{
col[i][j]=++co;
}
}
}
}
for (int i=1; i<=r; i++)
{
for (int j=1; j<=c; j++)
{
if (map[i][j]=='*')
{
addEdge(row[i][j],col[i][j]);
}
}
}
}
int dfs(int x)
{
for (int i=head[x]; i!=-1; i=E[i].nex)
{
if (!vis[E[i].to])
{
vis[E[i].to]=true;
if (pre[E[i].to]==-1||dfs(pre[E[i].to]))
{
pre[E[i].to]=x;
return 1;
}
}
}
return 0;
}
int main()
{
while (~scanf("%d%d",&r,&c))
{
init();
int sum=0;
for (int i=1; i<=ro; i++)
{
memset(vis,false,sizeof(vis));
if (dfs(i))
sum++;
}
printf("%d\n",sum);
}
return 0;
}