【WF2017】Mission Improbable

时间:2022-10-17 07:49:44

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 * ];
void addedge(int u, int v)
{
static int ecnt = ;
edges[ecnt].to = v;
edges[ecnt].next = head[u];
head[u] = ecnt++;
}
bool vis[maxn * ];
int mat[maxn * ];
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 = ; i <= r; i++)
{
if (!mat[i])
{
memset(vis, false, sizeof(vis));
dfs(i);
}
}
} int main()
{
ios::sync_with_stdio(false);
unsigned long long ans = ;
cin >> r >> c;
for (int i = ; i <= r; i++)
{
for (int j = ; 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] - ;
}
}
} // 若i行与j列的最大值相同,就可以把位置(i,j)放上这个最大值的数量的箱子,然后将i行与j列的其他能偷的箱子全部偷走
// 但是若(i,j)原来是0,这个位置就不能放箱子了
for (int i = ; i <= r; i++)
for (int j = ; j <= c; j++)
if (rmax[i] == cmax[j] && rmax[i] && grid[i][j])
addedge(i, j + maxn);
hungary(); for (int i = ; i <= r; i++)
if (rmax[i])
ans -= rmax[i] - ;
for (int i = ; i <= c; i++)
if (!mat[i + maxn] && cmax[i])
ans -= cmax[i] - ;
cout << ans;
return ;
}