zoukankan      html  css  js  c++  java
  • 棋盘游戏(二分图匹配)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1281

    棋盘游戏

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 3200    Accepted Submission(s): 1897


    Problem Description
    小 希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是 Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。
    所以现在 Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量 多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解 决这个问题么?
     
    Input
    输入包含多组数据,
    第一行有三个数N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盘的高、宽,以及可以放“车”的格子数目。接下来的K行描述了所有格子的信息:每行两个数X和Y,表示了这个格子在棋盘中的位置。
     
    Output
    对输入的每组数据,按照如下格式输出:
    Board T have C important blanks for L chessmen.
     
    Sample Input
    3 3 4 1 2 1 3 2 1 2 2 3 3 4 1 2 1 3 2 1 3 2
     
    Sample Output
    Board 1 have 0 important blanks for 2 chessmen. Board 2 have 3 important blanks for 3 chessmen.
     
    Author
    Gardon
     
    Source
     
    题解: 这种题有一种特殊的建图方法,然后就可以将问题巧妙的转换成二分图匹配的问题,然后用匈牙利算法解题即可: 将没一行看成是左边一个点,没一列看成是右边的一个点,如果这个点可以放棋子的话,就将其横纵坐标之间建立一条双向边,然后寻找最大的二分图匹配(相当于是每一个横行和纵行都只有一个焦点,而且使得焦点总数最多) 但是这个题要注意要求的是重要点的个数,及如果去掉这个点会影响最后焦点总数的点,所以,每次去掉一条边做一边二分图匹配,看最后,如果对结果有影响,则重要点个数加一
    下面介绍匈牙利算法:
    二分图匹配的匈牙利算法: 条件,可以将结点分成左右两组,两组之间有边,同一组之间没有边,求一对一的匹配数最多有多少,如果对于任意一个树来说,可以通过黑白染色的方法将图中的结点分成两份
    dfs 的算法思路: 对于左边任意一个点来说,找到一条增广路,注意是找到一条增广路就停止寻找(边的条数是奇数,而且是一条虚线开始,虚实向间的折线,以虚线结尾——实线是已经匹配上的线段,虚线表示未匹配)然后将这条增广路上的实线和虚线对调,这样就实现了匹配数加1,然后遍历左边的所有点,然后得到所有的增广路的条数就是最后的结果(因为每找到一条增广路就可以使得总匹配数加1)
    也可以换种方式理解: 一个左边的人B去右边找朋友,如果他找到了一个没有匹配过的人,那么就直接和她做朋友,而如果这个人C有了一个朋友A了,那么就从这个朋友A开始用同样的方式看他还能不能找到其他的朋友,如果可以的话 ,让A去找其他的人做朋友,这样B就可以C做朋友了(可以这么想,如果A另外寻找的D仍已经匹配过了,就再看D可以找到一个未匹配的吗,如果D找到的E仍然是匹配过的就再看E是否可以和其他人做朋友,这样一直找下去,知道找到了一个可以换一个人做朋友的,然后依次换掉,这样最开始的
    现在考虑如何去掉一条边,可以在边的结构体中加上一个bool 参数标记这条边是不是还在,详见代码
     1 #include<cstdio>
     2 #include<cstring>
     3 using namespace std;
     4 #define N 210
     5 struct Edge{
     6     int to;
     7     bool in;
     8     int next;
     9 }edge[N*N];
    10 int head[N];
    11 int Enct;
    12 bool visited[N*N];
    13 int link[N];
    14 void init()
    15 {
    16     memset(head,-1,sizeof(head));
    17     Enct = 0;
    18 }
    19 void add(int from , int to)
    20 {
    21     edge[Enct].to = to;
    22     edge[Enct].next = head[from];
    23     edge[Enct].in = true;
    24     head[from] = Enct++;
    25     edge[Enct].to = from;
    26     edge[Enct].in = true;
    27     edge[Enct].next = head[to];
    28     head[to] = Enct++;
    29 }
    30 bool find(int u)
    31 {
    32     for(int i = head[u] ; i!=-1 ; i = edge[i].next)
    33     {
    34         if(edge[i].in ==false) continue;
    35         int v = edge[i].to;
    36         if(visited[v]) continue;
    37         visited[v] = true;
    38         if(link[v] == -1||find(link[v]))
    39         {
    40             link[v] = u;//回溯,每次都将边反转
    41             //printf("%d ",link[v]);
    42             return true;
    43         }
    44     }
    45     return false;
    46 }
    47 int k;
    48 int n;
    49 int solve()
    50 {
    51     memset(link,-1,sizeof(link));
    52     int res = 0;
    53     for(int i = 1 ;i <= n ;i++)//注意点的编号是从1开始的
    54     {
    55         memset(visited,0,sizeof(visited));
    56         visited[i] = true;
    57         if(find(i)){ res++; 
    58              //printf("%d ",link[i]);
    59              //puts("");
    60         }
    61     }
    62     return res;
    63 }
    64 int main()
    65 {
    66     int m;
    67     int c = 0;
    68     while(~scanf("%d%d%d",&n,&m,&k))
    69     {
    70         c++;
    71         init();
    72         for(int i = 0 ; i < k ;i++)
    73         {
    74             int x, y;
    75             scanf("%d%d",&x,&y);
    76             add(x,y+n);
    77         }
    78         int sum = solve();
    79         int ans = 0 ;
    80         for(int i = 0 ;i < k ; i++)
    81         {
    82             edge[i<<1|1].in = false;
    83             edge[i<<1].in = false;
    84             int tm = solve();
    85             if(tm!=sum) ans++;
    86             edge[i<<1|1].in = true;
    87             edge[i<<1].in = true;
    88         }
    89         printf("Board %d have %d important blanks for %d chessmen.
    ",c,ans,sum);
    90     }
    91     return 0;
    92 }
     
  • 相关阅读:
    如何提升Web前端性能?
    简述Linux开机启动流程
    教你玩转Linux系统目录结构
    FreeBSD虚拟机 VMware Tools 安装教程
    何为KVM克隆和快照
    Knockout 新版应用开发教程之Computed Observables
    Knockout 新版应用开发教程之Observable与computed
    前端MVC框架Backbone 1.1.0源码分析系列
    Knockout 新版应用开发教程之创建view models与监控属性
    看看国外的javascript题目,你能全部做对吗?
  • 原文地址:https://www.cnblogs.com/shanyr/p/4761748.html
Copyright © 2011-2022 走看看