2021牛客多校第三场C题

题意:给定一个n×n的矩形,这些矩形中有m个空位可以放非负数(必须放),给定所有数字的最大值k(不一定取到),再给出每一行的最大值bi,每一列的最大值ci,求矩阵中所有数字和的最小值

 

以样例来讲,这是一个5×5的矩形,带*的是可以填数的格子,我们把最大的数也标出来

                                     

然后我们先考虑他的最大值5,正常来说,5是第五行的最大值,第五列的最大值,所以他对这个矩形的贡献理应是(1+1)*5,但是事实上我们发现第五行第五列的交点那个地方是可以放数字的,那么如果我们把5放在交点上,就同时满足了条件,这时的贡献就是(1+1-1)*5

之后看次大值4,对于四因为交点的格子不能填数,所以第四行必须有一个格子填4,第四列也必须有一个4,贡献就是(1+1)*4。然后就一直这样填下去

 由此,假设某个最大值k要在m行取到,在n列取到,则以这些行列构成点,如果行列交点可以放,就画一条边,这样就形成了二分图,那么我们求得二分图的最大匹配数t,这些行列对矩形最大的贡献就由(m+n)*k降为(m+n-t)*k

之后就一直从大到小遍历完,结果相加就行

代码贴上:

#include <bits/stdc++.h>
using namespace std;
const int N = 2010, M = 800010;
int n, m, k;
int h[N], ne[N * N],idx,match[N],e[N*N];
bool st[N];
int ma[N][N];
int ml[N],nr[N];//记录边或者列的数字

struct Nod
{
	int x, y;
}line[N],row[N];

bool cmp(Nod a, Nod b)
{
	return a.y > b.y;
}

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool find(int x)
{
	for (int i = h[x]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!st[j])
		{
			st[j] = true;
			if (!match[j] || find(match[j]))
			{
				match[j] = x;
				return true;
			}
		}

	}
	return false;
}

int main()
{
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
	{
		line[i].x = i;
		cin >> line[i].y;
	}
	sort(line + 1, line + n + 1, cmp);
	for (int i = 1; i <= n; i++)
	{
		row[i].x = i;
		cin >> row[i].y;
	}
	sort(row + 1, row + n + 1, cmp);
	while (m--)
	{
		int x, y;
		cin >> x >> y;
		ma[x][y] = 1;
	}
	int ans = 0;
	for (int i = 1,j=1; i <= n||j<=n;)//从最大到最小依次取出行列
	{
		int now = max(line[i].y, row[j].y);
		int mm = 0,nn = 0;
		memset(ml, 0, sizeof(ml));
		memset(nr, 0, sizeof(nr));
		while (i<=n&&line[i].y == now) 
		{
			ml[++mm] = line[i].x;
			i++;
		}
		while (j<=n&&row[j].y == now)
		{
			nr[++nn] = row[j].x;
			j++;
		}
		memset(h, -1, sizeof(h));
		memset(ne, 0, sizeof(ne));
		memset(e, 0, sizeof(e));
		memset(match, 0, sizeof(match));
		idx = 0;
		for (int p = 1; p <= mm; p++)
		{
			for (int q = 1; q <= nn; q++)
			{
				//cout << ml[p] << ' ' << nr[q] << endl;
				if (ma[ml[p]][nr[q]])//可以加边 ,判断的下标是原地图的下标 
				{
					add(p, q);//注意加边的下标是新生成的m’,n' 
					//cout << p << ' ' << q << endl;
				}
			}
		}
		int t=0;
		for (int p = 1; p <= mm; p++)
		{
			memset(st, 0, sizeof(st));
			if (find(p))
			{
				t++;
			}
		}
		//cout << mm << ' ' << nn << endl;
		ans += ((mm + nn - t) * now);
	}
	cout << ans << endl;
	return 0;
}

/*
Minimum grid
我们称bi和ci为行限定数和列限定数,统称限定数.
考虑最大的那个限定数,不妨设为x;找到行、列限定数是x的那些行和列组成的子矩形.
显然,其他数都不能满足x的要求.
假设这个子矩形 n'行m' 列,我们把每行建一个点,每列建一个点,可以填数的位置建一条边,得到一个二分
图;这个二分图的最大匹配数就是我们能节省出的x的个数.
那么这个?矩形对答案的贡献就为(n'+m'-t)*x; 
解决了这些限定数以后考虑次大的限定数 ,注意到次大的数可以“放进”限定数是最大的数的那些行列中,但是这
样也不能节省任何的y;因此还是建二分图求最大匹配,然后算贡献.
这样把每个限定数的二分图都建出来跑一遍匹配,算一下贡献即可.
*/ 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值