棋盘游戏
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1510 Accepted Submission(s): 867
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么?
第一行有三个数N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盘的高、宽,以及可以放“车”的格子数目。接下来的K行描述了所有格子的信息:每行两个数X和Y,表示了这个格子在棋盘中的位置。
Board T have C important blanks for L 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 }