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. 截断数组
题目描述
解题思路
题目要求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. 搭档
题目描述
解题思路
本题一共有两种做法
做法一:贪心 + 双指针
仅给出代码,贪心证明过于繁杂不再给出
简而言之,对两个数组进行排序,因为从小到大枚举的话一定可以获得最大的匹配数
做法二:匈牙利算法(二分图最大匹配)
这道题用此算法的关键在于如何建边
根据题目给出的条件,魅力值之差绝对值不超过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;
}