poj 2446 Chessboard (最大二分匹配)

来源:互联网 发布:android 移动数据开关 编辑:程序博客网 时间:2024/06/06 16:38

题意 : 给出一个矩形棋盘,其中包含一些不可到达的方格,问能否用长度为2的骨牌填满。

开始往搜索剪枝上想,复杂度太大(  O(2^N),其中N为棋盘面积  )。这题的骨牌长度特殊,抛去搜索,有特殊的方法可用,将棋盘上的所有格子划分成两个结点集合,再将两个集合的邻接的结点连边,构成二分图,问题就转化为了求该图的最大二分匹配。

为什么可以转化成二分图来做,我是这样想的,一个骨牌覆盖了棋盘上的两格,它们的行列之和一定是一个为奇数,一个为偶数。可以按照棋盘上格子的行列之和的奇偶将其分成两个结点集合,再将相邻的节点连边,这样二分图中的每条边就代表了这两个节点在棋盘上对应的格子可以被一个骨牌覆盖,整个二分图就代表了所有可放骨牌的情况,如下图:

左边展示了二分图所代表的棋盘状态,右边是一种可行解。

#include <iostream>using namespace std;// 常量const int N = 32; // 最大边长const int Dir[4][2] = { {-1,0}, {1,0}, {0,-1}, {0,1} };//--------------------------------- 对象 ----------------------------------//// 输入对象int h, w, k; // 棋盘高, 宽, 空洞数int board[N][N]; // 棋盘// 支撑算法对象bool g[N*N][N*N]; // 保存图int n1, n2; // 节点集合V1的节点数, V2的节点数, 即图的高和宽            // V1保存奇数点, V2保存偶数点int M[N*N]; // M[i]:与V2[i]匹配的V1中的点序号,如果没有M[i]=-1bool visit[N*N]; // visit[i]:V2[i]访问标记//-------------------------------------------------------------------------//// 函数/**  建立从V1 -> V2的有向图*/void Build_Graph(){/* 划分V1,V2集合 */for( int i = 0; i < h; ++ i )for( int j = 0; j < w; ++ j )if( board[i][j] != -1 ) {if( (i+j)&1 ) board[i][j] = n1 ++; // 奇else board[i][j] = n2 ++; // 偶}/* 连边 */for( int i = 0; i < h; ++ i )for( int j = 0; j < w; ++ j )if( (i+j)&1 && board[i][j] != -1 ) {/* 点(i,j)相邻4个方向 */for( int t = 0; t < 4; ++ t ) {int nr = i + Dir[t][0];int nc = j + Dir[t][1];if( nr >= 0 && nr < h && nc >= 0 && nc < w && // 未出界board[nr][nc] != -1 )                     // 不为空洞g[ board[i][j] ][ board[nr][nc] ] = 1;    // 连单向边,V1->V2}}}/**  寻找从V1[x]开始的增广路*  若存在,返回true; 反之false*/bool Find( const int x ){for( int i = 0; i < n2; ++ i )if( g[x][i] && !visit[i] ) {visit[i] = true;if( M[i] == -1 || Find(M[i]) ) {M[i] = x;return true;}}return false;}/**  匈牙利算法求最大二分匹配数*  返回最大二分匹配数*/int Hungary(){int num = 0;memset( M, -1, sizeof(M) ); // V2与V1不相连for( int i = 0; i < n1; ++ i ) {memset( visit, 0, sizeof(visit) );if( Find(i) ) ++ num; // 找到一条增广路}return num;}int main(){cin >> h >> w >> k;for( int i = 0; i < k; ++ i ) {int r, c;cin >> c >> r;board[r-1][c-1] = -1;}if( (h * w - k)&1 ) cout << "NO\n";else {n1 = n2 = 0;memset( g, 0, sizeof(g) );Build_Graph();             // 建图int num = Hungary();       // 求最大二分匹配if( num * 2 == h * w - k ) cout << "YES\n";else cout << "NO\n";}return 0;}


原创粉丝点击