题目:http://community.topcoder.com/stat?c=problem_statement&pm=1931&rd=4709
题目的难点在于将问题抽象成最大流问题,这个题目可以看成是 最大二分图匹配问题(maximum bipartite-matching problem),行可以看成二分图中的 顶点集A,列可以看成二分图中顶点集B,下面的问题就是最大匹配A,B。使用最大流算法求解。
代码如下:
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <bitset>
#include <string>
#include <vector>
#include <stack>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cstring>
#include <ctime>
#include <climits>
using namespace std;
#define CHECKTIME() printf("%.2lf\n", (double)clock() / CLOCKS_PER_SEC)
typedef pair<int, int> pii;
typedef long long llong;
typedef pair<llong, llong> pll;
#define mkp make_pair
/*************** Program Begin **********************/
bool cut[605][605]; // 位置是否可放置
int cap[605][605]; // 每条边的容量
int from[605]; // 保留最短路径上到达该点的上一个顶点
bool v[605]; // 访问数组
const int INF = 1000000;
class RookAttack {
public:
int rows, cols;
// 使用增广路径法求最大流,返回值为0时说明残留图中不存在增广路径
int bfs()
{
memset(v, 0, sizeof(v));
memset(from, -1, sizeof(from));
queue <int> Q;
int start = rows, end = rows + cols + 1; // 添加两个超级顶点,将问题转化为单源单汇
Q.push(start);
v[start] = true;
int where;
// bfs 求最短路径
bool ok = false;
while (!Q.empty()) {
where = Q.front();
Q.pop();
for (int i = 0; i <= rows + cols + 1; i++) {
if (cap[where][i] > 0 && !v[i]) { // 有边且点未被访问
from[i] = where;
v[i] = true;
if (i == end) {
ok = true;
break;
}
Q.push(i);
}
}
if (ok) {
break;
}
}
// 求最短路径上的最小容量
int path_cap = INF;
where = end;
while (from[where] != -1) {
int pre = from[where];
path_cap = min(path_cap, cap[pre][where]);
where = pre;
}
// 更新残留图,cap[]数组
where = end;
while (from[where] != -1) {
int pre = from[where];
cap[pre][where] -= path_cap;
cap[where][pre] += path_cap;
where = pre;
}
return (path_cap == INF ? 0 : path_cap);
}
int howMany(int rows, int cols, vector <string> cutouts) {
int res = 0;
this->rows = rows;
this->cols = cols;
memset(cut, 0, sizeof(cut));
// 对字符串进行处理
string S;
for (int i = 0; i < cutouts.size(); i++) {
S += cutouts[i] + ", ";
}
for (int i = 0; i < S.size(); i++) {
if (',' == S[i]) {
S[i] = ' ';
}
}
int r, c;
stringstream ss(S);
// 每行、每列代表一个顶点,行顶点编号为0...rows - 1,超级源点为rows;
// 列顶点编号为rows + 1 ... rows + cols,超级汇点为rows + cols + 1.
while (ss >> r >> c) {
cut[r][c + rows + 1] = true;
}
memset(cap, 0, sizeof(cap));
for (int i = 0; i < rows; i++) {
for (int j = rows + 1; j < rows + cols + 1; j++) {
if (!cut[i][j]) {
cap[i][j] = 1; // 有边,容量为1
}
cap[j][rows + cols + 1] = 1; // 每个汇点到超级汇点的容量为1
}
cap[rows][i] = 1; // 超级源点到每个源点的容量为1
}
int add;
while ( (add = bfs()) != 0 ) { // 不断调用BFS,直到没有残留图中没有增广路径为止
res += add;
}
return res;
}
};
/************** Program End ************************/
如果使用dfs来找最短路径的话,代码更简洁,如下:
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <bitset>
#include <string>
#include <vector>
#include <stack>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <cstring>
#include <ctime>
#include <climits>
using namespace std;
#define CHECKTIME() printf("%.2lf\n", (double)clock() / CLOCKS_PER_SEC)
typedef pair<int, int> pii;
typedef long long llong;
typedef pair<llong, llong> pll;
#define mkp make_pair
/*************** Program Begin **********************/
bool cut[305][305]; // 位置是否可放置
int col_match[305];
bool v[305]; // 访问数组
class RookAttack {
public:
int rows, cols;
int find_path(int where)
{
if (-1 == where) { // 找到一条增广路
return 1;
}
for (int i = 0; i < cols; i++) {
if (cut[where][i] || v[i]) {
continue;
}
v[i] = true;
if (find_path(col_match[i])) {
col_match[i] = where; // 相当于更新残留图
return 1;
}
}
return 0;
}
int howMany(int rows, int cols, vector <string> cutouts) {
int res = 0;
this->rows = rows;
this->cols = cols;
memset(cut, 0, sizeof(cut));
// 对字符串进行处理
string S;
for (int i = 0; i < cutouts.size(); i++) {
S += cutouts[i] + ", ";
}
for (int i = 0; i < S.size(); i++) {
if (',' == S[i]) {
S[i] = ' ';
}
}
int r, c;
stringstream ss(S);
while (ss >> r >> c) {
cut[r][c] = true;
}
memset(col_match, -1, sizeof(col_match));
for (int i = 0; i < rows; i++) {
memset(v, 0, sizeof(v));
res += find_path(i); // dfs找增广路径
}
return res;
}
};
/************** Program End ************************/