pku3715 Blue and Red

快顶不住了,理解了多少,先写出来再说;

题意:

军队里面要展开实战演练,已经将n个士兵分成了两队,红队和蓝队。指挥官认为,如果两个属于不同阵营中的士兵是好朋友,那么演练就会有感情因素的干扰。所以要选出最少的人,以排除感情因素的干扰。问要选出多少人?选哪些人?

分析: 就题目而已,很明显是一道求最小覆盖点集的题目,又最小覆盖点数==最大匹配数,所以,单求出队人数的话,就是一道完完全全的求二分最大匹配的题目,可是,要怎么求出最小覆盖点集呢?而且还要按字典序输出?问题就在这里了,在求出最大匹配数的基础,从第一个点开始枚举,若去掉这个点,此时,最小覆盖数减少(即从该点匹配的点为起点,找不到增广路径),则该点属于最小覆盖点集,应该输出,且去掉,否则,恢复该点,继续枚举;

因为从第一个点开始枚举,所以保证了字典序输出。

整个算法的过程还不算是理解了,因为有一个坎一直过不去,是我对寻找增广路径的过程还理解的不够透彻吗?当然枚举到一个点时,匹配的点已经删掉了,这时候若调用深搜,返回值肯定为假,即找不到增广路,这时候该点不是要输出了吗?可是有没有加一个判断都过了,是代码在处理的时候已经考虑了这种情况了吗?还是根本不会出现这种情况?求大牛解释

#include<iostream>
using namespace std;
int wet[205][205];
int mx[205], my[205]; 
char vstd[205]; 
char del[205]; //是否属于最小点覆盖集
int grp[205]; //0, 1
int nv, ne;
int pathx(int x) 
{
    int y; 
    if(del[x]) return 0; //如果该点属于最小点覆盖集,就不用再找了.     
    for(y = 1; y <= nv; y++)
    {
          if(wet[x][y] && !vstd[y] && !del[y])
          {
                       vstd[y] = 1;
                       if(my[y]==-1 || pathx(my[y]))
                       {
                                    my[y] = x;
                                    mx[x] = y;
                                    return 1;
                       }
          }
    }
    return 0;
}
int pathy(int y)
{
    int x; 
    if(del[y]) return 0; 
    for(x = 1; x <= nv; x++)
    {
         if(wet[x][y] && !vstd[x] && !del[x])
         {
                      vstd[x] = 1;
                      if(mx[x]==-1 || pathy(mx[x]))
                      {
                                   my[y] = x;
                                   mx[x] = y;
                                   return 1;
                      }
         } 
    }
    return 0;
}
void MaxMatch()
{
     int i, x, y, res = 0;  
     memset(del, 0, sizeof(del));
     memset(mx, -1, sizeof(mx));
     memset(my, -1, sizeof(my));
     for(x = 1; x <= nv; x++)
     {
           memset(vstd, 0, sizeof(vstd));
           res += pathx(x);
     }
     printf("%d", res);
     for(i = 1; i <= nv; i++)
     {
           if(grp[i]==0)  //如果是X[]的点 
           {
                        y = mx[i];
                        if(y==-1)
						continue;                       
                        mx[i] = -1;  //去掉这条最大匹配边,寻找完后不用加回来,为什么? 
                        my[y] = -1;
					//	if(del[y]) continue; 个人觉得这里应该判断一下的,可是不判断也一样过
                        del[i] = 1;  //假设它是最小点覆盖集中一点                       
                        memset(vstd, 0, sizeof(vstd));
                        if(!pathy(y)) //如果没有增广路径. 
                              printf(" %d", i-1); //那么该点就是最小点覆盖集中的点. 
                        else del[i] = 0;  
           }
		   else
           {
               x = my[i];
               if(x==-1) continue;               
               mx[x] = -1;
               my[i] = -1;	
			   if(del[x]) continue;
               del[i] = 1;  
               memset(vstd, 0, sizeof(vstd));
               if(!pathx(x))
                       printf(" %d", i-1);
               else del[i] = 0;
           }
     }
     printf("\n");
}
int main()
{
    int cas, i, s, t;
    scanf("%d", &cas);
    while(cas--)
    {
                scanf("%d %d", &nv, &ne);                
                memset(grp, 0, sizeof(grp));
                memset(wet, 0, sizeof(wet));              
                for(i = 1; i <= nv; i++)
                      scanf("%d", &grp[i]);                
                for(i = 1; i <= ne; i++)
                {
                      scanf("%d %d", &s, &t);
                      s++;t++;
                      if(grp[s]==0 && grp[t]==1) wet[s][t] = 1;
                      if(grp[s]==1 && grp[t]==0) wet[t][s] = 1;
                }               
                MaxMatch();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/nanke/p/2151456.html