寒假第二讲总结

一、代码AC截图

二、题目反思总结

Easy1

思路:二分查找套模板

#include<iostream>
using namespace std;
#include<cstring>
#include<vector>
int main()
{   
    int n, q;
    int x;
    cin >> n;//n是数组大小
    vector<int>a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    cin >> q;
    for (int i = 0; i < q; i++)
    {
        cin >> x;
        int l = 0;
        int r= n - 1;
        while (l < r)
        {
            int mid =( r + l) / 2;
            if (a[mid] >= x)r = mid;
            else l = mid + 1;
        }
        if (a[l] != x) cout << "No" << endl;
        else cout << "Yes" << endl;
    }
    return 0;
}

这里补充总结一下二分的模板

核心点在于:

1check(mid)的条件判断写法

2l还是r等于mid,如果是l=mid在mid的初始条件补上+1,r=mid不需要

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

作者:yxc
链接:https://www.acwing.com/blog/content/277/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 例题:

 核心代码段

#include<iostream>
using namespace std;
#include<cstring>
#include<vector>
int main()
{   
    const int N= 100010;
    int n, m;
    int q[N];
    for (int i = 0; i < n; i++)
        scanf("%d", &q[i]);
    while (m--)
    {
        int x;
        scanf("%d", &x);
        int l = 0, r = n - 1;
        while (l < r)
        {
            int mid = (l + r) / 2;
            if (q[mid] >= x) r = mid;//先找第一个大于等于x的数字的位置
            else l = mid + 1;
        }
        if (q[l] != x) cout << "-1 -1" << endl;//如果二分出来的结果不等于x,也就是二分找到的最小的大于x的数不为x,那么x就不在该数组里面
        else
        {
            cout << l << ' ';//把该数组中x的第一个位置输出
            //接着找x的最大下角标
            int l = 0, r = n - 1;
            while (l < r)
            {
                int mid = (l + r + 1) / 2;
                if (q[mid] <= x)l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
}

 Easy2

思路:A-B=C,A,B都是最开始的数组a【】里面的,那么可以让C+B=A,把A值放在新数组b【】里面,然后在b【】数组里面查找a【】数组里面的值。用了两个函数,分别查找1第一个大于等于x的位置2最后一个大于等于x的位置,如果第一个都没有找到则说明没有x,那么可以跳过2的查找节省时间,如果第一个有找到,那么把两次下角标相减的差加1就可以得到b数组里面对应的a数组值的个数,用循环遍历a数组的值,并在b数组中一一查找求个数再把个数累加起来

#include<iostream>
using namespace std;
#include<cstring>
#include<vector>
#include <algorithm>
long long bsearch_1(long long l, long long r,const vector<long long>& b,long long target)
{
    while (l < r)
    {
        int mid = (l + r)/2;
        if (b[mid]>=target) r = mid;   
        else l = mid + 1;
    }
    if (b[l] !=target)
        return -1;
    return l;
}
long long bsearch_2(long long l, long long r,const vector<long long>& b,long long target)
{
    while (l < r)
    {
        int mid =( l + r + 1)/2;
        if (b[mid]<=target) l = mid;
        else r = mid - 1;
    }
    return l;
}
int main()
{   
    long long flag1, flag2;
    long long cnt = 0;
    long long n, c;
    cin >> n >> c;
    vector<long long> a(n);
    vector<long long> b(n);
    for (long long i = 0; i < n; i++)
        cin >> a[i];
    for (long long i = 0; i < n; i++)
        b[i] = a[i] + c;
    sort(b.begin(), b.end());
    for (long long k = 0; k < n; k++)
    {
        flag1 = bsearch_1(0, n - 1, b, a[k]);
        if (flag1 < 0)
        {
            continue;
        }
        else
        {
            flag2 = bsearch_2(0, n - 1, b, a[k]);
        }
        cnt += flag2 - flag1 + 1;
    }
    cout << cnt;
}

特别注意的点

1数组b要记得排序一下才能二分查找

2结果开longlong才能过测试点,特别感谢

 

 Medium1

思路:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool check(int mid, vector<vector<int>> &table,int n,int k)
{
	int cnt = 0;
	for (int i = 0; i < n; i++)
	{
		cnt += (table[i][0] / mid) * (table[i][1] / mid);
	}
	return cnt >= k;
}
int main()
{
	int n, k;
	cin >> n >> k;
    vector<vector<int>> table(n, vector<int>(2));
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < 2; j++)
			cin >> table[i][j];
	}
	int l = 1, r = 1e5;
	while (l < r)
	{
		int mid = (l + r+1) / 2;
		if (check(mid, table, n, k))l = mid;
		else r = mid - 1;
	}
		cout << l << endl;
	
	return 0;
}

  Medium2

思路:答案序列具备单调性!!!

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool check(long long mid,vector<long long>&a,vector<long long>&b,long long n,long long m)
{
	long long cnt = 0;
	for (long long i = 0; i < n; i++)
	{
		if (a[i] < mid)
		{
			if (mid - a[i] <= b[i])
				cnt += mid - a[i];
			else
				return false;
		}

	 }
	return cnt <= m;

}
int main()
{
	long long n, m;
	cin >> n >> m;
	vector<long long>a(n);
	vector<long long>b(n);
	for (long long i = 0; i < n; i++)
		cin >> a[i];
	for (long long i = 0; i < n; i++)
		cin >> b[i];
	long long l = 0, r = 1e9;
	while (l < r)
	{
		long long mid = (l + r+1) / 2;
		if (check(mid, a, b, n, m)) l = mid;
		else r = mid - 1;
	}
	cout << l<<endl;
	return 0;
}

第一次写超时了,找了好久才发现是套的模板忘记+1然后死循环啦。。真是无语了。。被自己无语到。。也还是要多注意细节。

三、个人心得

二分答案真的好巧妙啊巧妙,只是有时候从具体问题中转化写check条件的时候还挺抽象的。模板一定要背对啊啊。。有公式就是快。。但如果背错了就悲哀了。。这几天再整理一下思路再找几个同类题多写写吧。第二次打卡get,坚持啊请坚持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值