几道二分匹配

额,累死了,,快十天了没更新博客啦,这些天一直在做二分匹配,截止到刚才终于把hdu Index By Type里面的match做完啦。。好吧,一题一题开始说。

http://acm.hdu.edu.cn/showproblem.php?pid=2236

无题II

一般遇到使最大最小差值最小的问题,要想到二分答案,然后枚举下限,对于每一种情况判断是否可行。

因为每行没列都选一个,所以用到二分匹配,行在A中,列在B中,这样每一个数都代表从A到B的一条边。

然后判断在这样的一种情况下,能否找得到一组完备匹配。

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 105

 5 int map[N][N],n,used[N];

 6 bool vis[N][N],visit[N];

 7 int s[10005];

 8 bool getnum(int i)

 9 {

10     int j;

11     for(j=1;j<=n;j++)

12     {

13         if(vis[i][j] && !visit[j])

14         {

15             visit[j]=1;

16             if(used[j]==-1 || getnum(used[j]))

17             {

18                 used[j]=i;

19                 return 1;

20             }

21         }

22     }

23     return 0;

24 }

25 bool Match(int l,int r)

26 {

27     int i,j,count;

28     memset(vis,0,sizeof(vis));

29     for(i=1;i<=n;i++)

30     {

31         for(j=1;j<=n;j++)

32         {

33             if(map[i][j]>=l && map[i][j]<=r) vis[i][j]=1;

34         }

35     }

36     count=0;

37     memset(used,-1,sizeof(used));

38     for(i=1;i<=n;i++)

39     {

40         memset(visit,0,sizeof(visit));

41         if(getnum(i)) count++;

42     }

43     if(count==n) return 1;

44     return 0;

45 }

46 int cmp(const void *a,const void *b)

47 {

48     return *(int *)a - *(int *)b;

49 }

50 int main()

51 {

52     int i,j,ncase,Dnum,k;

53     int right,left,mid,ans;

54     scanf("%d",&ncase);

55     while(ncase--)

56     {

57         scanf("%d",&n);

58         k=0;

59         for(i=1;i<=n;i++)

60             for(j=1;j<=n;j++)

61             {

62                 scanf("%d",&map[i][j]);

63                 s[++k]=map[i][j];

64             }

65         qsort(s+1,n*n,sizeof(s[1]),cmp);

66         k=1;

67         for(i=2;i<=n*n;i++)

68             if(s[i]!=s[i-1]) s[++k]=s[i];

69         Dnum=s[k]-s[1];

70         left=0;right=Dnum;

71         while(left<=right)

72         {

73             mid=(left+right)/2;

74             for(i=1;s[i]+mid<=s[k];i++)

75             {

76                 if(Match(s[i],s[i]+mid)) break;

77             }

78             if( i<=k && s[i]+mid<=s[k])

79             {

80                 ans=mid;

81                 right=mid-1;

82             }

83             else left=mid+1;

84         }

85         printf("%d\n",ans);

86     }

87     return 0;

88 }

http://acm.hdu.edu.cn/showproblem.php?pid=1533

Going Home

很裸的一个km,n个人回到n个房子里面,两点之间的权值是他们的曼哈顿距离,让你求 每个人都回到房子里所需走的距离和最小,一般km都是求最大权值和,所以可以把边权取相反数,然后km,之后求得的值再取相反数。

View Code
  1 # include<stdio.h>

  2 # include<string.h>

  3 # include<stdlib.h>

  4 # define N 105

  5 # define INF 0xfffffff

  6 int n,k,m;

  7 int map[N][N];

  8 struct node{

  9     int x,y;

 10 }s1[N],s2[N];

 11 int lx[N],ly[N];

 12 bool visx[N],visy[N];

 13 int slock[N],used[N];

 14 int dis(int i,int j)

 15 {

 16     int ans,num;

 17     num=s1[i].x-s2[j].x;

 18     if(num<0) num=-num;

 19     ans=s1[i].y-s2[j].y;

 20     if(ans<0) ans=-ans;

 21     return ans+num;

 22 }

 23 int Min(int a,int b)

 24 {

 25     return a<b?a:b;

 26 }

 27 bool find(int x)

 28 {

 29     int j,t;

 30     visx[x]=1;

 31     for(j=1;j<=k;j++)

 32     {

 33         if(visy[j]) continue;

 34         t=lx[x]+ly[j]-map[x][j];

 35         if(t==0)

 36         {

 37             visy[j]=1;

 38             if(used[j]==-1 || find(used[j]))

 39             {

 40                 used[j]=x;

 41                 return 1;

 42             }

 43         }

 44         else if(t<slock[j]) slock[j]=t;

 45     }

 46     return 0;

 47 }

 48 void KM()

 49 {

 50     int i,j,d;

 51     memset(ly,0,sizeof(ly));

 52     memset(used,-1,sizeof(used));

 53     for(i=1;i<=k;i++)

 54     {

 55         for(j=1;j<=k;j++)

 56             slock[j]=INF;

 57         for(;;)

 58         {

 59             memset(visx,0,sizeof(visx));

 60             memset(visy,0,sizeof(visy));

 61             if(find(i)) break;

 62             d=INF;

 63             for(j=1;j<=k;j++)

 64                 if(!visy[j]) d=Min(d,slock[j]);

 65 

 66             for(j=1;j<=k;j++)

 67                 if(visx[j]) lx[j]-=d;

 68 

 69             for(j=1;j<=k;j++)

 70                 if(visy[j]) ly[j]+=d;

 71                 else slock[j]-=d;

 72         }

 73     }

 74 }

 75 int main()

 76 {

 77     int i,j,k1,k2,sum;

 78     char ch[105];

 79     while(scanf("%d%d",&n,&m)!=EOF)

 80     {

 81         if(n==0 && m==0) break;

 82         k1=k2=0;

 83         for(i=0;i<n;i++)

 84         {

 85             scanf("%s",ch);

 86             for(j=0;j<m;j++)

 87             {

 88                 if(ch[j]=='H') 

 89                 {

 90                     s1[++k1].x=i;

 91                     s1[k1].y=j;

 92                 }

 93                 else if(ch[j]=='m')

 94                 {

 95                     s2[++k2].x=i;

 96                     s2[k2].y=j;

 97                 }

 98             }

 99         }

100         k=k1;

101         for(i=1;i<=k;i++)

102         {

103             lx[i]=-INF;

104             for(j=1;j<=k;j++)

105             {

106                 map[i][j]=-1*dis(i,j);

107                 if(map[i][j]>lx[i]) lx[i]=map[i][j];

108             }

109         }

110         KM();

111         sum=0;

112         for(j=1;j<=k;j++)

113             sum+=map[used[j]][j];

114         printf("%d\n",-sum);

115     }

116     return 0;

117 }

http://acm.hdu.edu.cn/showproblem.php?pid=2255

奔小康赚大钱

还是一个km,直接模板,没什么说的了。

 

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 305

 5 # define INF 0xfffffff

 6 int n;

 7 int map[N][N];

 8 int lx[N],ly[N];

 9 bool visx[N],visy[N];

10 int slock[N],used[N];

11 bool find(int x)

12 {

13     int j,t;

14     visx[x]=1;

15     for(j=1;j<=n;j++)

16     {

17         if(visy[j]) continue;

18         t=lx[x]+ly[j]-map[x][j];

19         if(t==0)

20         {

21             visy[j]=1;

22             if(used[j]==-1 || find(used[j]))

23             {

24                 used[j]=x;

25                 return 1;

26             }

27         }

28         else if(t<slock[j]) slock[j]=t;

29     }

30     return 0;

31 }

32 int Min(int a,int b)

33 {

34     return a<b?a:b;

35 }

36 void KM()

37 {

38     int i,j,d;

39     memset(ly,0,sizeof(ly));

40     memset(used,-1,sizeof(used));

41     for(i=1;i<=n;i++)

42     {

43         for(j=1;j<=n;j++) slock[j]=INF;

44         for(;;)

45         {

46             memset(visx,0,sizeof(visx));

47             memset(visy,0,sizeof(visy));

48             if(find(i)) break;

49             d=INF;

50             for(j=1;j<=n;j++)

51                 if(!visy[j]) d=Min(d,slock[j]);

52             for(j=1;j<=n;j++)

53                 if(visx[j]) lx[j]-=d;

54             for(j=1;j<=n;j++)

55                 if(visy[j]) ly[j]+=d;

56                 else slock[j]-=d;

57         }

58     }

59 }

60 int main()

61 {

62     int i,j,sum;

63     while(scanf("%d",&n)!=EOF)

64     {

65             for(i=1;i<=n;i++)

66             {

67                 lx[i]=0;

68                 for(j=1;j<=n;j++)

69                 {

70                     scanf("%d",&map[i][j]);

71                     if(map[i][j]>lx[i]) lx[i]=map[i][j];

72                 }

73             }

74             KM();

75             sum=0;

76             for(j=1;j<=n;j++)

77                 sum+=map[used[j]][j];

78             printf("%d\n",sum);

79     }

80     return 0;

81 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=1083

Courses

有P门课程,N位学生,每一位学生可以代表一门他已经访问过的课程,问最后每一门课是否都有学生代表。

二分匹配,判断最大匹配是否为P。

 

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 305

 5 # define P 105

 6 bool map[P][N],vis[N];

 7 int used[N];

 8 int n,p;

 9 bool getnum(int i)

10 {

11     int j;

12     for(j=1;j<=n;j++)

13     {

14         if(!vis[j] && map[i][j])

15         {

16             vis[j]=1;

17             if(used[j]==-1 || getnum(used[j]))

18             {

19                 used[j]=i;

20                 return 1;

21             }

22         }

23     }

24     return 0;

25 }

26 int main()

27 {

28     int i,j,ncase,num,ans;

29     scanf("%d",&ncase);

30     while(ncase--)

31     {

32         scanf("%d%d",&p,&n);

33         memset(map,0,sizeof(map));

34         for(i=1;i<=p;i++)

35         {

36             scanf("%d",&num);

37             for(j=1;j<=num;j++)

38             {

39                 scanf("%d",&ans);

40                 map[i][ans]=1;

41             }

42         }

43         if(p>n) {printf("NO\n");continue;}

44         memset(used,-1,sizeof(used));

45         for(i=1;i<=p;i++)

46         {

47             memset(vis,0,sizeof(vis));

48             if(!getnum(i)) break;

49         }

50         if(i==p+1) printf("YES\n");

51         else printf("NO\n");

52     }

53     return 0;

54 }

 

 http://acm.hdu.edu.cn/showproblem.php?pid=1281

棋盘游戏

因为车对同一行同一列的相互攻击,所以每一行每一列只能放一个棋子。

求出最大匹配后,因为要判断每一点是否为关键点,枚举最大匹配中的每一点,把他去掉,再求最大匹配数看是否和原来的相等,如果相等,那该点就不是一个关键点,否则是一个关键点。

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 105

 5 int n,m,K;

 6 bool map[N][N],vis[N];

 7 int used[N],ans[N];

 8 struct node{

 9     int x,y;

10 }s[N*N];

11 bool getnum(int i)

12 {

13     int j;

14     for(j=1;j<=m;j++)

15     {

16         if(!vis[j] && map[i][j])

17         {

18             vis[j]=1;

19             if(used[j]==-1 || getnum(used[j]))

20             {

21                 used[j]=i;

22                 return 1;

23             }

24         }

25     }

26     return 0;

27 }

28 int main()

29 {

30     int i,j,t=0,count,num,a,b,ans1;

31     while(scanf("%d%d%d",&n,&m,&K)!=EOF)

32     {

33         t++;

34         memset(map,0,sizeof(map));

35         for(i=1;i<=K;i++)

36         {

37             scanf("%d%d",&a,&b);

38             map[a][b]=1;

39             s[i].x=a;

40             s[i].y=b;

41         }

42         memset(used,-1,sizeof(used));

43         count=0;

44         for(i=1;i<=n;i++)

45         {

46             memset(vis,0,sizeof(vis));

47             if(getnum(i)) count++;

48         }

49         num=0;

50         for(i=1;i<=K;i++)

51         {

52             a=s[i].x;

53             b=s[i].y;

54             if(used[b]==a)

55             {

56                 for(j=1;j<=m;j++)

57                     ans[j]=used[j];

58                 map[a][b]=0;

59                 memset(used,-1,sizeof(used));

60                 ans1=0;

61                 for(j=1;j<=n;j++)

62                 {

63                     memset(vis,0,sizeof(vis));

64                     if(getnum(j)) ans1++;

65                 }

66                 if(ans1<count) num++;

67                 map[a][b]=1;

68                 for(j=1;j<=m;j++)

69                     used[j]=ans[j];

70             }

71         }

72     printf("Board %d have %d important blanks for %d chessmen.\n",t,num,count);

73     }

74     return 0;

75 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=1498

50 years, 50 colors

扎气球,每一次可以扎破同一行或者同一列相同颜色的气球。对每一种颜色问在扎了k次之后能否把该颜色的气球全扎破。二分匹配,对于每一种颜色,还是把行放在A集合中,列放在B集合中,然后求最小点集覆盖。

最小点集覆盖=最大匹配。

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 105

 5 int n,k;

 6 bool vis[N],map[N][N];

 7 int used[N],num[55];

 8 struct node{

 9     int x,y;

10 }s[55][10005];

11 bool getnum(int i)

12 {

13     int j;

14     for(j=1;j<=n;j++)

15     {

16         if(!vis[j] && map[i][j])

17         {

18             vis[j]=1;

19             if(used[j]==-1 || getnum(used[j]))

20             {

21                 used[j]=i;

22                 return 1;

23             }

24         }

25     }

26     return 0;

27 }

28 int main()

29 {

30     int i,j,kk,mm[N],ans,a,b,count;

31     while(scanf("%d%d",&n,&k)!=EOF)

32     {

33         if(n==0 && k==0) break;

34         memset(num,0,sizeof(num));

35          for(i=1;i<=n;i++)

36          {

37              for(j=1;j<=n;j++)

38              {

39                  scanf("%d",&ans);

40                  num[ans]++;

41                  s[ans][num[ans]].x=i;

42                  s[ans][num[ans]].y=j;

43              }

44          }

45          kk=0;

46          for(i=1;i<=50;i++)

47          {

48              if(num[i]<=k) continue;

49              memset(map,0,sizeof(map));

50              for(j=1;j<=num[i];j++)

51              {

52                  a=s[i][j].x;

53                  b=s[i][j].y;

54                  map[a][b]=1;

55              }

56              memset(used,-1,sizeof(used));

57              count=0;

58              for(j=1;j<=n;j++)

59              {

60                  memset(vis,0,sizeof(vis));

61                  if(getnum(j)) count++;

62              }

63              if(count>k)  mm[++kk]=i;

64          }

65          if(kk==0) printf("-1\n");

66          else

67          {

68              printf("%d",mm[1]);

69              for(i=2;i<=kk;i++)

70                  printf(" %d",mm[i]);

71              printf("\n");

72          }

73     }

74     return 0;

75 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=3605

Escape

二分图的多重匹配,把原来的used数组改成二维的即可。

由于一个vis数组开大了,各种超时。。

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 100005

 5 int num[12];

 6 int used[12][N],ans[12];

 7 bool map[N][12],vis[12];

 8 int n,m;

 9 bool getnum(int i)

10 {

11     int j,k;

12     for(j=1;j<=m;j++)

13     {

14         if(!vis[j] && map[i][j])

15         {

16             vis[j]=1;

17             if(num[j]<ans[j])

18             {

19                 num[j]++;

20                 used[j][num[j]]=i;

21                 return 1;

22             }

23             else 

24             {

25                 for(k=1;k<=ans[j];k++)

26                 {

27                     if(getnum(used[j][k])) 

28                     {

29                         used[j][k]=i;

30                         return 1;

31                     }

32                 }

33             }

34         }

35     }

36     return 0;

37 }

38 int main()

39 {

40     int i,j,sum;

41     while(scanf("%d%d",&n,&m)!=EOF)

42     {

43         for(i=1;i<=n;i++)

44             for(j=1;j<=m;j++)

45                 scanf("%d",&map[i][j]);

46             sum=0;

47             for(i=1;i<=m;i++)

48             {

49                 scanf("%d",&ans[i]);

50                 sum+=ans[i];

51             }

52             if(sum<n) {printf("NO\n");continue;}

53             memset(used,-1,sizeof(used));

54             memset(num,0,sizeof(num));

55             for(i=1;i<=n;i++)

56             {

57                 memset(vis,0,sizeof(vis));

58                 if(!getnum(i)) break;

59             }

60             if(i<=n) printf("NO\n");

61             else printf("YES\n");

62     }

63     return 0;

64 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=1151

Air Raid

//路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点

最小路径覆盖=顶点数-最大匹配

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 125

 5 bool map[N][N],vis[N];

 6 int used[N];

 7 int n,m;

 8 bool getnum(int u)

 9 {

10     int i;

11     for(i=1;i<=n;i++)

12     {

13         if(!vis[i] && map[u][i])

14         {

15             vis[i]=1;

16             if(used[i]==-1 || getnum(used[i]))

17             {

18                 used[i]=u;

19                 return 1;

20             }

21         }

22     }

23     return 0;

24 }

25 int main()

26 {

27     int i,ncase,count;

28     int a,b;

29     scanf("%d",&ncase);

30     while(ncase--)

31     {

32         scanf("%d%d",&n,&m);

33         memset(map,0,sizeof(map));

34         while(m--)

35         {

36             scanf("%d%d",&a,&b);

37             map[a][b]=1;

38         }

39         memset(used,-1,sizeof(used));

40         count=0;

41         for(i=1;i<=n;i++)

42         {

43             memset(vis,0,sizeof(vis));

44             if(getnum(i)) count++;

45         }

46         printf("%d\n",n-count);

47     }

48     return 0;

49 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=1669

Jamie's Contact Groups

二分答案+多重匹配,开始用邻接表写的,一直WA,最后改成邻接矩阵竟然对了,不知道用邻接表为什么会错,很是不解。

View Code
 1 # include<stdio.h>

 2 # include<string.h>

 3 # include<stdlib.h>

 4 # define N 1005

 5 # define M 505

 6 int n,m,mid;

 7 int used[M][N],Num[M];

 8 bool vis[M],map[N][M];

 9 bool getnum(int u)

10 {

11     int j,v;

12     for(j=0;j<m;j++)

13     {

14         v=j;

15         if(!map[u][v]) continue;

16         if(vis[v]) continue;

17         vis[v]=1;

18         if(Num[v]<mid)

19         {

20             Num[v]++;

21             used[v][Num[v]]=u;

22             return 1;

23         }

24         for(j=1;j<=Num[v];j++)

25         {

26             if(getnum(used[v][j])) 

27             {

28                 used[v][j]=u;

29                 return 1;

30             }

31         }

32     }

33     return 0;

34 }

35 int main()

36 {

37     int i,j;

38     int flag,num,ans;

39     int l,r;

40     char str[20000];

41     while(scanf("%d%d",&n,&m)!=EOF)

42     {

43         if(n==0 && m==0) break;

44         memset(map,0,sizeof(map));

45         for(i=0;i<n;i++)

46         {

47             scanf("%s",str);

48             gets(str);

49             flag=0;

50             for(j=0;str[j];j++)

51             {

52                 if(str[j]>='0' && str[j]<='9' && flag==0)

53                 {

54                     num=str[j]-'0';

55                     flag=1;

56                 }

57                 else if(str[j]>='0' && str[j]<='9')

58                 {

59                     num*=10;

60                     num+=str[j]-'0';

61                 }

62                 else if(str[j]==' ')

63                 {

64                     if(flag==1) map[i][num]=1;

65                     flag=0;

66                 }        

67             }

68                 if(flag==1) map[i][num]=1;

69         }

70         l=n/m;

71         if(l*m!=n) l++;

72         r=n;

73         while(l<=r)

74         {

75             mid=(l+r)/2;

76             memset(Num,0,sizeof(Num));

77             for(i=0;i<n;i++)

78             {

79                 memset(vis,0,sizeof(vis));

80                 if(!getnum(i)) break;

81             }

82             if(i==n) {ans=mid;r=mid-1;}

83             else l=mid+1;

84         }

85         printf("%d\n",ans);

86     }

87     return 0;

88 }

 

http://acm.hdu.edu.cn/showproblem.php?pid=2413

 

Against Mammoths

我也是用二分答案做的,但是有一组例子都跑不出来。。。

1 1

0 40000 40000 39999

40000

结果应该是1600040000。

难道是我水过了????

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(二分)