题意
给一个网格图,每个格子有若干个方块。求最少需要多少方块来摆放,使得三视图和原来相同。
思路
- 一个基本的想法是,尽量将重复的行列用一个来覆盖。
- 考察一组可行的摆放方案,并且将每一行每一列中最高值中的某一个选定。容易发现,其余没有选定又存在方块的地方取1是优的。这样调整之后,这组摆放所需的代价就是 ,cnt1是俯视图不为0的个数,A[i][j]是所有选定的位置。
- cnt1是定值,现在考虑最小化后面的求和。
- 这个求和号的一个上界就是所有行列限制的和(可能达不到)
- 减小这个求和只有一个方法:使用恰当的摆放方法使得某些行-列一起被满足(放在他们交点处)。
- 只有限制相同的行列可以一起满足,于是乎做一个最大匹配,此时求和号取得最小值。
- 并且在这些交点处放置对应的个数之后,一定能构造出原三视图(考虑某个全局最大限制,一定有一对成功的匹配。此时这个行列的其他位置可以用于满足其余限制)。因此这就是原问题的答案、
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 110;
int T,n,m;
int a[N][N], dx[N], dy[N], b[N][N];
int to[N][N], link[N];
ll ans,cnt;
int vis[N];
bool go(int x) {
vis[x]=1;
for(int y=1;y<=m;y++)if(to[x][y]){
if(link[y]==0){
link[y]=x;
return 1;
}else{
if(vis[link[y]]==0&&go(link[y])){
link[y]=x;
return 1;
}
}
}
return 0;
}
int main() {
freopen("views.in","r",stdin);
// freopen("views.out","w",stdout);
for(cin>>T;T;T--){
cnt=0;
cin>>n>>m;
memset(dx,0,sizeof dx);
memset(dy,0,sizeof dy);
ans = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&b[i][j]);
dx[i]=max(dx[i],b[i][j]);
dy[j]=max(dy[j],b[i][j]);
cnt += b[i][j];
}
}
memset(to,0,sizeof to);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if (b[i][j] && dx[i] == dy[j]) {
to[i][j] = 1;
}
ans += b[i][j] != 0;
}
}
memset(link,0,sizeof link);
for(int i=1;i<=n;i++)if(dx[i]){
ans+=dx[i]-1;
}
for(int i=1;i<=m;i++)if(dy[i]){
ans+=dy[i]-1;
}
for (int i=1;i<=n;i++) {
memset(vis,0,sizeof vis);
if(go(i))
ans-=dx[i]-1;
}
cout << cnt - ans << endl;
}
}