「Acwing」第37场周赛 题解

A - 4296. 合适数对

解题思路

由于 n <= 1000,直接将x从1枚举到1000即可,用公式求出y
将判断条件写为x * a <= n,这样就可以保证y也是正数
TIPS:如果x * a > n的话,y * b就一定是负数=> y是负数

代码
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, a, b;
    cin >> n >> a >> b;

    for (int x = 0; x * a <= n; x ++ )
        if ((n - a * x) % b == 0)
        {
            int y = (n - a * x) / b;
            cout << "YES" << endl;
            cout << x << ' ' << y << endl;
            return 0;
        }

    cout << "NO" << endl;
    return 0;
}

B - 4297. 截断数组

题目描述

avatar
avatar

解题思路

题目要求sum1 == sum3,并且sum1尽可能大;
我们可以预处理前缀和s[];
我们在遍历i时,可以得到区间[0~i]的前缀和视作sum1,然后将每次得到的sum1都加入集合
然后同时通过s[n] - s[i - 1]得出sum3的值,如果在Set中已经存在与sum3相等的值sum1
直接输出结果后结束程序

问:问什么i要从2开始遍历?遍历之前为什么还要把s[1]单独加入集合?

因为如果i = 1,有s[n] - s[0],相当于sum3独占区间[1,n]的所有元素,根本不可能做到sum1和sum3相等

问:如何保证第一次找到的结果就一定是sum1的最大值呢?

因为i是从小到大枚举的,sum3是由s[n] - s[i - 1]求出的,可以看出sum3的值随着i增大而一直在变小,因为区间在减小
而每次在集合中查找的都是sum3是否存在,所以一旦找到,必然是sum3的最大值,而sum1==sum3.即也是sum1的最大值

问:sum1是从小变大的,sum3是从大变小的,为什么能找到答案?是如何做到不重不漏的

尽管sum3的值一直在变大,sum1一直在变小,但是枚举的过程中,sum1的末尾元素永远是sum3的首元素的前一位
这就相当于中间没有任何空隙(sum2元素个数为0)。这样只能做到判断sum2个数等于0的情况下sum1是否等于sum3
但由于sum1的值每次都被加入集合,即便sum2个数大于0,也能做到不重不漏!

TIPS:本题也拥有双指针做法,具体点击此处

代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
typedef long long ll;

ll n, s[N];
unordered_set<ll>Set;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i];
        s[i] += s[i - 1];
    }
    
    Set.insert(s[1]);
    for (int i = 2; i <= n; i++) 
    {
        ll s3 = s[n] - s[i - 1];
        if (Set.count(s3))
        {
            cout << s3;
            return 0;
        }
        Set.insert(s[i]);
    }
    cout << 0;
    return 0;
}

C - 4298. 搭档

题目描述

avatar

解题思路

本题一共有两种做法
做法一:贪心 + 双指针
仅给出代码,贪心证明过于繁杂不再给出
简而言之,对两个数组进行排序,因为从小到大枚举的话一定可以获得最大的匹配数

做法二:匈牙利算法(二分图最大匹配)
这道题用此算法的关键在于如何建边
根据题目给出的条件,魅力值之差绝对值不超过1
于是建边条件为if (abs(sba[i] - sbb[j])<=1) add(i,j)
对于二分图最大匹配可以参照模板题

代码一 (贪心 + 双指针做法)
#include <bits/stdc++.h>
using namespace std;
const int N = 110;

int n, m;
int a[N], b[N];
bool match1[N], match2[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    cin >> m;
    for (int i = 0; i < m; i++) cin >> b[i];
    
    sort(a, a + n);
    sort(b, b + m);
    
    int res = 0;
    for (int i = 0, j = 0; i < n; i++) 
    {
        while (j < m && a[i] > b[j] + 1)    j++;
        if (j < m && a[i] >= b[j] - 1) 
        {
            res++;
            j++;
        }
    }
    
    cout << res;
    return 0;
}
代码二 (匈牙利做法)
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 10010;

int n, m, a[N], b[N], idx;
int h[N], ne[M], e[M], match[N];
bool vis[N];

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 (!vis[j])  
        {
            vis[j] = true;  // 标记为正在访问,避免下面find递归时重复访问等情况
            if (match[j] == 0 || find(match[j]))  // 如果没有被匹配,或者已经被匹配过,但是能让match[j]换一个“对象”
            {
                match[j] = x;
                return true;  // 就匹配成功
            }
        }
    }
    return false;
}

int main()
{
    memset(h, -1, sizeof h);
    
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    cin >> m;
    for (int i = 1; i <= m; i++) cin >> b[i];
    
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (abs(a[i] - b[j]) <= 1)  // 建边条件
                add(i, j);
    
    int res = 0;
    for (int i = 1; i <= n; i++)
    {
        memset(vis, false, sizeof vis);  // 注意每次初始化的是vis[], 而不是match[]
        if (find(i)) res++;  // 成功匹配就次数加1
    }
        
    cout << res;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wiz1code(算法号)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值