P6931 [ICPC2017 WF]Mission Improbable

P6931 [ICPC2017 WF]Mission Improbable

给定一个 r \times cr×c 的平面,在上面摆有一些箱子。我们可以得到他的三视图(如下图,左边矩阵上的值为平面上每一位摆放的箱子个数,右边三个视图为正视图,俯视图,左视图):

你可以拿走一些箱子,和重新排列这些箱子的位置,你想知道,最多能拿走多少个箱子,使得这些箱子重新排列后正视图,俯视图,左视图不变?

比如上面这个例子,下面这种拿走 99 个箱子后的重新排列方式也是可以的:

1 \le r,c \le 1001≤r,c≤100,平面上每一个位置的箱子个数在 [0,10^9][0,10
9
] 内。


エラー発生:边数应该是\(maxn * maxn\) 的。。 开小了查到半夜 哇巨蠢


Solution

贪心加二分图匹配

首先我们满足俯视图要求, 在有格子的地方全放上1先
贪心, 每一行/每一列, 我们只需要堆一次最高的箱子就行了
进一步贪心, 我们想让某一竖的箱子, 即对长的最大值产生贡献, 又对宽的最大值产生贡献。
那么跑一次二分图匹配, 看一下哪行哪列的箱子可以对应一下, 并且交点处满足俯视图可以放箱子, 连边跑匈牙利就行

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long
#define REP(i, x, y) for(LL i = (x);i <= (y);i++)
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 110;
LL head[maxn * 2],nume = 1;
struct Node{
    LL v,dis,nxt;
    }E[maxn * maxn];
void add(LL u,LL v,LL dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
	
LL x, y;
LL map[maxn][maxn];
LL x_max[maxn], y_max[maxn];
LL used[maxn * 2], mat[maxn * 2];
bool match(LL u){//二分图匹配
	for(LL i = head[u];i;i = E[i].nxt){
		LL v = E[i].v;
		if(!used[v]){
			used[v] = 1;
			if(!mat[v] || match(mat[v])){
				mat[v] = u;
				mat[u] = v;
				return 1;
				}
			}
		}
	return 0;
	}
void Hungary(){
	REP(i, 1, x){
		if(!mat[i]){
			memset(used, 0, sizeof(used));
			match(i);
			}
		}
	}
LL ans;
void init(){
	x = RD(), y = RD();
	REP(i, 1, x)REP(j, 1, y){
		map[i][j] = RD();
		if(map[i][j] != 0)ans += map[i][j] - 1;//满足俯视图,贪心全拿走
		}
	REP(i, 1, x)REP(j, 1, y)x_max[i] = max(x_max[i], map[i][j]);
	REP(i, 1, y)REP(j, 1, x)y_max[i] = max(y_max[i], map[j][i]);
		
	REP(i, 1, x){
		REP(j, 1, y){
			if(x_max[i] == y_max[j] && map[i][j] != 0 && x_max[i] != 0)
				add(i, maxn + j + 1, 1);
			}
		}
	}
void work(){
	Hungary();
	
	REP(i, 1, y)
		if(y_max[i])//保证下加的1
			ans -= y_max[i] - 1;//先把竖着的放上去
			

	REP(i, 1, x){
		if(!mat[i] && x_max[i])
			ans -= x_max[i] - 1;
		}
	cout<<ans<<endl;
	}
int main(){
	init();
	work();
	return 0;
	}
posted @ 2021-02-17 01:17  Tony_Double_Sky  阅读(70)  评论(0编辑  收藏  举报