大概题意:在一个R*C大小的网格中,网格上面放着一些目标,可以在网格外面发射子弹,子弹可以沿着垂直或则水平的方向射出,并打掉该路径上的所有目标,问最少需要多少子弹,并把这些子弹的位置输出。
我们先来确定非匹配边的情况,对于一条非匹配边,我们可以确定这条边肯定有一个点或两个点都属于匹配点 , 对于只有一个点属于匹配点 ,那么我们肯定要选这个匹配点 , 因此 , 我们只需要在求出最大匹配之后 , 再用X集合中的非匹配点去发展匈牙利树 ,然后我们标记X集合和Y集合中已经走过的点 , 最后 X 中没有标记的点和 Y 中已经标记的点 , 就是子弹的位置 。由于我们是用非匹配点去发展匈牙利树 , 因此如果存在边 , 那么我们就一定要选这条边的另外一个点 , 并且这个点一定是匹配点 ,那么和这个点相连的所有边就已经都覆盖了 , 再往后面的匈牙利树 , 我们就能很容易得出了。
对于匹配边 , 在我们后面已经得到的匈牙利树中 ,如果匹配边已经在这个树种 , 那么我们可以确定这条边肯定已经被覆盖 , 对于不在匈牙利树中的匹配边 ,我们可以确定这条匹配边上的X集合中的点 , 肯定没有被标记 , 那么根据上面的输出规则 , 我们可以确定用这中方法得到的点 ,肯定已经覆盖了所有边。
解法:每发射一个子弹,我们就能把某一行或者某一列的目标都清楚掉,因此我们每一行、每一列看成一个目标,并把这一行、列中的目标都标同一个号,那么我们就能通过同一个位置的目标来得到这个位置所在行、列的关系(在它们之间连一条边),我们要求的就转化为,用最少的点去覆盖所有的边,也就是最小点覆盖 , 也就是这个二分图的最大匹配。
子弹的位置:首先我们要知道 , 对于二分匹配算法 , 一定会遍历所有边 , 也就是说所有边都会属于一个或多个匈牙利树 。我们先把这些边分为匹配边和非匹配边 。
代码:
#define MAXN 1010
#define INF 1000000
#define max(x , y) (x)>(y)?(x):(y)
#define min(x , y) (x)<(y)?(x):(y)
int n , m , k; // 记录每条边的权值
int pre[MAXN] ; // 记录和y中点匹配的点是哪个点
vectorgrap[MAXN];
int wr[MAXN] , wc[MAXN];
int cx[MAXN] , cy[MAXN];
int bzx[MAXN] , bzy[MAXN];
void init()
{
memset(wr , 0 , sizeof(wr));
memset(wc , 0 , sizeof(wc));
for(int i = 1; i<= n; i++)
{
grap[i].clear();
}
memset(cx , -1 ,sizeof(cx));
memset(cy , -1 ,sizeof(cy));
}
bool match(int i) // 寻找增广路
{
bzx[i] = 1;
for(int j = 0; j< grap[i].size(); j++)
{
int v = grap[i][j];
if(!pre[v])
{
pre[v] =1;
bzy[v] = 1;
if(cy[v]== -1 || match(cy[v]))
{
cx[i] = v;
cy[v] = i;
return true;
}
}
}
return false;
}
int km()
{
int i ;
memset(bzx , 0 ,sizeof(bzx));
memset(bzy , 0 , sizeof(bzy));
int res = 0;
for(i = 1; i<= n; i++)
{
if(cx[i] == -1&& wr[i])
{
memset(pre, 0 , sizeof(pre));
if(match(i)) res += 1;
}
}
return res;
}
int main()
{
while(scanf("%d %d %d" ,&n , &m , &k) !=EOF)
{
if(n+m+k == 0) break;
init();
int i , x , y ;
for(i = 1; i <= k; i++)
{
scanf("%d %d" , &x ,&y);
grap[x].push_back(y);
wr[x] = wc[y] = 1;
}
x = km();
km();
printf("%d" , x);
for(i = 1; i <= n; i++)
if(!bzx[i] && wr[i]) printf(" r%d" , i);
for(i = 1; i <= m; i++)
if(bzy[i] && wc[i]) printf(" c%d" , i);
cout<<endl;
}
return 0;
}