棋盘游戏

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


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.

解题思路:
      刚开始也不知道可以用二分图来做,想了半天不知道怎么写,参考网上大牛的代码。我从来没有写过二分图的程序,这个可以算是个开始吧,现学现卖!
讲解前,我假定大家对二部图的定义以及一些基本定理有一定的了解,不用知道的太多,只要知道什么是二部图就行。首先,将一个坐标为(x,y)的单元格看成是x和y的匹配,那么二分图的两个集合X={1,...,N},Y={1,...,M},即X是横坐标的集合,Y是纵坐标的集合。
      题中要求若(x,y)放一个棋子,则x行和y列都不允许再放棋子,在这种条件下放最多的棋子;可以将问题转化为X和Y的一个最大匹配。(这个地方不明白的童鞋要仔细想)
下面讲解如何求解一个二部图map[N][M]的最大匹配。使用易于理解和编程的匈牙利算法。对于X集合中的每一个x(即map中的每一行进行遍历),依次遍历每一个y,如果y还没有匹配,则用一个一位数组记录match[y]=x;表示y和x已经匹配,如果y已经匹配(即match[y]=k),那么递归这个过程,判断k是否可以和Y中的其他节点匹配,如果可以,则修改match[y]=x;如果不行,那么就跳过这个y,查找Y集合中的其他节点。
算法的大致思想就是这样,不知我讲的是否清楚,下面是针对杭电的这一道题运用匈牙利算法求解的程序。

 1 #include<iostream>
 2 using namespace std;
 3 #define MAX 1010
 4 bool map[MAX][MAX]={false};
 5 int match[MAX]={0};
 6 bool visit[MAX]={0};
 7 bool dfs(int n,int M)
 8 {
 9     int i=0,j=0;
10     for(j=1;j<=M;j++)
11     {
12         if(visit[j] || !map[n][j]) continue;
13         visit[j]=true;
14         if(!match[j] || dfs(match[j],M))
15         {
16             match[j]=n;
17             return true;
18         }
19     }
20     return false;
21 }
22 int solve(int N,int M)
23 {
24     int i=0,j=0,cnt=0;
25     memset(match,0,sizeof(match));
26     for(i=1;i<=N;i++)
27     {
28         memset(visit,0,sizeof(visit));
29         if(dfs(i,M)) cnt++;
30     }
31     return cnt;
32 }
33 int main()
34 {
35     int N,M,K,i=0,j=0,x,y,max=0,cnt=0,num=1;
36     while(cin>>N>>M>>K)
37     {
38         cnt=max=0;
39         memset(map,false,sizeof(map));
40         for(i=0;i<K;i++)
41         {
42             cin>>x>>y;
43             map[x][y]=1;
44         }
45         max=solve(N,M);
46         for(i=1;i<=N;i++)
47         {
48             for(j=1;j<=M;j++)
49             {
50                 if(map[i][j])
51                 {
52                     map[i][j]=0;
53                     if(max>solve(N,M)) cnt++;
54                     map[i][j]=1;
55                 }
56             }
57         }
58         cout<<"Board "<<(num++)<<" have "<<cnt<<" important blanks for "<<max<<" chessmen."<<endl;
59     }
60 
61     return 1;
62 }
View Code

 

posted on 2013-05-17 12:52  一路狂奔的人  阅读(189)  评论(0编辑  收藏  举报