【二分查找】网格 (web)

【二分查找】网格 (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^91n,m105,1k109

思路

我们的目标是求出填数之和的最大值。
所以每个位置填的数字要尽可能地接近所给的上限值(r[i] 和 c[i])。

首先对列上限c[]从小到大排序。

对于每一行,我们找到使得c[pos] <= r[i]的右边界pos,这样我们就可以将每一行pos及以前的位置全部填上每一列对应的c[j],后面全部填r[i]
如果说我们找到的位置pos == m并且这个位置c[m] < x[i],那么不合题意,输出-1.
或者说到最后找到的所有pos中最大的posmax_pos < m, 那么也不符合题意,输出-1.

注意事项

  1. 代码中间步骤存在相乘操作,可能超过int的最大范围;
    最后结果可能超过long long.
    因此建议全都用unsigned long long.
  2. 二分查找边界时注意: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值