【二分查找】网格 (web)
题目描述
LinZhengYu 拿出了一个 n 行 m 列的网格,他想要在每个位置上填一个正整数。
LinZhengYu 不希望填完数的网格太难看,于是就给出了他想要的每行每列上数的最大值,填完数的网格必须满足每行每列上数的最大值严格等于他所给出的最大值。(记第 i 行上数的最大值为正整数ri(1≤i≤n),第j列上的数的最大值为正整数ci(1≤j≤m) ,记r,c两数组中的数的最大值为k)。
你需要写一个程序,告诉他这样的填数方案是否存在。特别地,如果存在,您还需要告诉他所填数之和的最大值。
输入
第一行两个正整数n,m ,表示网格的行数和列数。
第二行n个正整数,第i个正整数表示ri 。
第三行m个正整数,第j个正整数表示cj。
输出
若存在填数方案,输出所填数之和的最大值。否则,输出-1。
样例输入
【样例1】
3 3
1 3 1
3 1 3
【样例2】
5 3
1 2 3 4 5
3 2 1
样例输出
【样例1】
13
【样例2】
-1
提示
样例1解释:
一种所填数之和最大的填数方案是:
第一行:1,1,1
第二行:3,1,3
第三行:1,1,1
样例2解释:不存在填数方案。
对于100%的数据,1≤n,m≤105,1≤k≤1091≤n,m≤10^5,1≤k≤10^91≤n,m≤105,1≤k≤109
思路
我们的目标是求出填数之和的最大值。
所以每个位置填的数字要尽可能地接近所给的上限值(r[i] 和 c[i]
)。
首先对列上限c[]
从小到大排序。
对于每一行,我们找到使得c[pos] <= r[i]
的右边界pos
,这样我们就可以将每一行pos
及以前的位置全部填上每一列对应的c[j]
,后面全部填r[i]
。
如果说我们找到的位置pos == m
并且这个位置c[m] < x[i]
,那么不合题意,输出-1
.
或者说到最后找到的所有pos
中最大的pos
即max_pos < m
, 那么也不符合题意,输出-1
.
注意事项
- 代码中间步骤存在相乘操作,可能超过
int
的最大范围;
最后结果可能超过long long
.
因此建议全都用unsigned long long
. - 二分查找边界时注意:
pos
也有可能为0
, 因为可能刚开始就有c[1] > r[i]
, 因此把0
纳入搜索范围是必要的.
代码
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
const int N = 1e5 + 10;
int n, m;
int r[N], c[N];
ull sum[N];
int max_pos = -1;
ull res;
void init()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &r[i]);
}
for (int i = 1; i <= m; i++)
{
scanf("%lld", &c[i]);
}
sort(c + 1, c + m + 1);
for (int i = 1; i <= m; i++)
{
sum[i] = sum[i - 1] + c[i];
}
}
int binarysearch(int i)
{
int L = 0, R = m;
while (L < R)
{
int mid =( L + R + 1) >> 1;
if (c[mid] <= r[i])
{
L = mid;
}
else
{
R = mid - 1;
}
}
return L;
}
void solve()
{
int pos = -1;
for (int i = 1; i <= n; i++)
{
pos = binarysearch(i);
if (pos == m && c[m] < r[i])
{
puts("-1");
return;
}
max_pos = max(pos, max_pos);
res += sum[pos];
res += 1ull * (m - pos) * r[i];
}
if (max_pos < m)
{
puts("-1");
return;
}
cout << res << "\n";
}
signed main()
{
init();
solve();
return 0;
}