jzoj 6428 立体几何(结论,二分图匹配)

题意

给一个网格图,每个格子有若干个方块。求最少需要多少方块来摆放,使得三视图和原来相同。
n 100 , c 1 0 9 n\leq 100,c\leq10^9

思路

  • 一个基本的想法是,尽量将重复的行列用一个来覆盖。
  • 考察一组可行的摆放方案,并且将每一行每一列中最高值中的某一个选定。容易发现,其余没有选定又存在方块的地方取1是优的。这样调整之后,这组摆放所需的代价就是 c n t 1 + A [ i ] [ j ] 1 cnt_1+\sum A[i][j]-1 ,cnt1是俯视图不为0的个数,A[i][j]是所有选定的位置。
  • cnt1是定值,现在考虑最小化后面的求和。
  • 这个求和号的一个上界就是所有行列限制的和(可能达不到)
  • 减小这个求和只有一个方法:使用恰当的摆放方法使得某些行-列一起被满足(放在他们交点处)。
  • 只有限制相同的行列可以一起满足,于是乎做一个最大匹配,此时求和号取得最小值。
  • 并且在这些交点处放置对应的个数之后,一定能构造出原三视图(考虑某个全局最大限制,一定有一对成功的匹配。此时这个行列的其他位置可以用于满足其余限制)。因此这就是原问题的答案、

O ( n 3 ) O(n^3)

#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;
	}
}

发布了266 篇原创文章 · 获赞 93 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/103076799
今日推荐