【WF2017】Mission Improbable
http://www.lydsy.com/JudgeOnline/problem.php?id=4950
对于俯视图很好解决,把所有不是0的位置拿到剩1就可以了。
对于正视图与侧视图,稍微想一下也能发现只要保持每行(和每列)箱子最多的那个位置不动就可以了。
但是可能存在一行有两个以上的点都是最大值,并且其中一点所在的列的最大值也和这行的最大值相等的情况。这时候只保持这一点不变,显然优于同时保持该行另一点和那一列的最大值那点不变要更优。
这时候就可以建二分图找最大匹配了:将若i行与j列的最大值相等且不为零,且(i,j)原本有箱子,就在i与r+j连边。
最后统计一下答案就可以了。
#include <iostream> #include <cstring> #define maxn 105 using namespace std; int r, c, grid[maxn][maxn]; int rmax[maxn], cmax[maxn]; struct { int to, next; } edges[maxn * maxn]; int head[maxn * 2]; void addedge(int u, int v) { static int ecnt = 1; edges[ecnt].to = v; edges[ecnt].next = head[u]; head[u] = ecnt++; } bool vis[maxn * 2]; int mat[maxn * 2]; bool dfs(int v) { for (int i = head[v]; i; i = edges[i].next) { int w = edges[i].to; if (!vis[w]) { vis[w] = true; if (!mat[w] || dfs(mat[w])) { mat[w] = v; mat[v] = w; return true; } } } return false; } void hungary() { for (int i = 1; i <= r; i++) { if (!mat[i]) { memset(vis, false, sizeof(vis)); dfs(i); } } } int main() { ios::sync_with_stdio(false); unsigned long long ans = 0; cin >> r >> c; for (int i = 1; i <= r; i++) { for (int j = 1; j <= c; j++) { cin >> grid[i][j]; if (grid[i][j]) { rmax[i] = max(rmax[i], grid[i][j]); cmax[j] = max(cmax[j], grid[i][j]); ans += grid[i][j] - 1; } } } // 若i行与j列的最大值相同,就可以把位置(i,j)放上这个最大值的数量的箱子,然后将i行与j列的其他能偷的箱子全部偷走 // 但是若(i,j)原来是0,这个位置就不能放箱子了 for (int i = 1; i <= r; i++) for (int j = 1; j <= c; j++) if (rmax[i] == cmax[j] && rmax[i] && grid[i][j]) addedge(i, j + maxn); hungary(); for (int i = 1; i <= r; i++) if (rmax[i]) ans -= rmax[i] - 1; for (int i = 1; i <= c; i++) if (!mat[i + maxn] && cmax[i]) ans -= cmax[i] - 1; cout << ans; return 0; }