题意:给定一个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;因此还是建二分图求最大匹配,然后算贡献.
这样把每个限定数的二分图都建出来跑一遍匹配,算一下贡献即可.
*/