我在接受采访时被问到这个问题。我正在修改这个问题以保护它不被明确表示为Google,但要点是:
您将获得N x M
网格。网格中的一些单元格是“邪恶的”(用数字1表示),其余的是“好”(用0表示)。您可以在每个N
行和每个M
列中放置激光,这些激光在打开时会杀死其各自行或列中的所有单元格,例如如果你有:
L1 L2 L3 L4
L5 0 1 0 0
L6 0 1 0 1
L7 0 1 1 0
L8 0 0 0 0
你可以打开(L2,L3,L4):
L1 L2 L3 L4
L5 0 x x x
L6 0 x x x
L7 0 x x x
L8 0 x x x
或者你可以打开(L2,L6,L7):
L1 L2 L3 L4
L5 0 x 0 0
L6 x x x x
L7 x x x x
L8 0 x 0 0
如果它杀死了所有邪恶的细胞,那么一组激光被打开称为“GoodConfig”。请注意,你可以随时打开一行或一列的所有激光并杀死所有东西,这将是“GoodConfig”,但开启激光是昂贵的,杀死好的细胞是不好的。
“GoodConfig”的最小尺寸是多少,即我们可以打开最少数量的激光直到杀死所有邪恶细胞?
什么是“GoodConfig”,可以最大限度地减少杀死的好细胞数量?
答案 0 :(得分:9)
这个问题可以重新表述为二分图上的最小顶点覆盖问题。
考虑一个图:顶点是行(一部分)和列(另一部分)。当且仅当相应的单元格row
是邪恶的时候,顶点col
和(row, col)
之间存在边缘。
我们现在的问题是找到一组具有最小可能尺寸的顶点,这样我们图形的每个边(前一个单元格)在我们的集合(行或列)中至少有一个顶点。
根据Koenig's Theorem,我们可以在二分图中找到最大匹配,然后恰好标记匹配的每个边的一个顶点,以便得到的顶点集覆盖上述意义上的图。特别是,最大匹配的大小等于最小顶点覆盖的大小。
答案 1 :(得分:2)
为了回答1)和2),我会采用以下方法,假设N或M很小(比如说< = 25)。
选择了一组行/列(以较小者为准)的暴力,并检查相应的成本添加列/行以涵盖所有内容。
假设N <= M,有2个 N 行,我们处理每个行的整个矩阵。该方法在O(2 N * N * M)中运行。
#define CONTAINS(mask, bit) (mask & (1 << bit))
void Solve(int matrix[MAX][MAX], int N, int M) {
vector<int> best;
int i, j, best_good_cells;
for (int rows_mask = 0; rows_mask < (1 << N); rows_mask++) {
vector<int> lasers;
int good_cells = 0;
for (j = 0; j < M; j++) {
bool add_column = false;
int good = 0;
for (i = 0; i < N; i++)
if (!CONTAINS(rows_mask, i)) {
if (matrix[i][j] == 0)
good++;
else
add_column = true;
}
if (add_column) {
lasers.push_back(j);
good_cells += good;
}
}
for (i = 0; i < N; i++)
if (CONTAINS(rows_mask, i))
lasers.push_back(M+i);
if (best.size() == 0 ||
best.size() > lasers.size() ||
(best.size()==lasers.size() && good_cells < best_good_cells)){
best = lasers;
best_good_cells = good_cells;
}
}
cout << "Select lasers:";
for (auto i: best)
cout << " " << i+1;
cout << endl;
}