【蓝桥杯】二分、前缀和、差分练习

之前 觉得二分挺好理解的,就没怎么花时间研究,直到上次校赛理解丘丘人那题,才知道二分原来不止于查找,果然蒟蒻就是蒟蒻

分巧克力

P8647 [蓝桥杯 2017 省 AB] 分巧克力 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这题的思路是对每一种可能的正方形边长进行讨论,观察数据(1≤Hi​,Wi​≤1e5),因此正方形可能的边长为1-1e5。对每一个边长,讨论能分成几个这样的正方形并用sum记录,如果sum大于等于人数k,即满足题意。

这里要理解一个长为h宽为w的长方形能分成(h/i)*(w/i)个边长为i的正方形。

暴力枚举

这里暴力枚举会WA两个点,为什么不是TLE,很困惑(在网上复制别人的暴力代码同样会WA那两个点)

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k;
int h[N],w[N];
int sum;
int res; 
signed main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&h[i],&w[i]); 
	}
	for(int i=1;i<=1e5;i++)//巧克力所有可能的边长 
	{
		sum=0;
		for(int j=1;j<=n;j++)
		{
			sum+=(h[j]/i)*(w[j]/i);//可以分成几个正方形 
		}
		if(sum>=k)
		{
			res=i;//保存边长 
		}else break;
	}
	cout<<res;
	return 0;
}

二分AC代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k;
int h[N],w[N];
int sum;
bool check(int x)
{
	sum=0;
	for(int i=1;i<=n;i++)
	{
		sum+=(h[i]/x)*(w[i]/x);
	}
	if(sum>=k) return true;
	else return false;
}
signed main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&h[i],&w[i]); 
	}
    //二分边长
	int l=1,r=1e5,res=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			res=mid;
			l=mid+1;
		}else r=mid-1;
	}
	cout<<res;
	return 0;
}

求和

P8772 [蓝桥杯 2022 省 A] 求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

有前缀和、差分,先简单了解一下这些算法

什么,差分是前缀和的升级版?那我们先了解一下前缀和

前缀和

在这里插入图片描述

 前缀和就是从位置1到位置i这个区间内的所有的数字之和

前缀和s的输入

for (int i = 1; i <= n; i ++ )
{
	cin >> a[i];
	s[i] = s[i - 1] + a[i];
}

前缀和的优势:以(o1)的时间复杂度得到某块区间的总和

在这里插入图片描述

前缀和【超详细讲解前缀和】_菜瓜变菜鸟的博客-CSDN博客

二维数组求前缀和

差分

 C++算法——差分_c++调用差分函数_是挺秃然的齐齐哦的博客-CSDN博客

 在这里插入图片描述

前缀和以及差分看这一篇就够了 - xbhog - 博客园 (cnblogs.com)

AC代码

这题知道了前缀和之后就很简单了(原来数组开小了也会RE啊

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
int a[N];
long long s[N];
long long sum;
signed main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		s[i]=s[i-1]+a[i];//求前缀和 
	}
	for(int i=1;i<=n-1;i++)
	{
		sum+=a[i]*(s[n]-s[i]); 
	}
	printf("%lld",sum);
	return 0;
}

借教室

P1083 [NOIP2012 提高组] 借教室 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

复杂度O(mn)直接t了

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m;//天数和订单数量
long long restroom[N];//第i天可借教室
long long b[N];
int res; 
signed main()
{
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=n;i++) scanf("%lld",&restroom[i]);
	int num=m; 
	while(m--)
	{
		long long d;
		int s,t;//租借数量,第几天开始和结束 
		scanf("%lld%d%d",&d,&s,&t);
		memset(b,0,sizeof b);
		b[s]+=-d,b[t+1]-=-d;
		for(int i=1;i<=n;i++)//i是天数 
		{
			b[i]=b[i-1]+b[i];
			restroom[i]+=b[i];
			if(restroom[i]<0)
			{
				cout<<-1<<endl;
				res=i;
				cout<<num-m;
				return 0;
			}
		}
	}
	cout<<res;
	return 0;
}

 AC代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
int n, m, ans;
//二分能满足多少订单(logm),每次差分+前缀和O(n)后扫一遍所有天数O(n),判断能否满足;
long long r[MAXN], cha[MAXN];
struct Order{
    long long d;
    int s, t;
}a[MAXN];
//检查能否满足1...num这些订单,如果能,返回true;如果不能,返回false;
bool check(int num)
{
    //差分.当然也可以用两个数组:一个差分数组,一个原数组;
    //做完操作后将差分数组做一遍前缀和再与原数组相加即可。
    for(int i = 1; i <= num; ++i){
        cha[ a[i].s ] += a[i].d;
        cha[ a[i].t+1 ] -= a[i].d;
    }
    for(int i = 1; i <= n; ++i){
        cha[i] += cha[i-1];
        if(cha[i] > r[i])return false;
    }        
    return true;
}

int main()
{    
    cin>>n>>m;
    for(int i = 1; i <= n; ++i)scanf("%d", &r[i]);
    for(int i = 1; i <= m; ++i)scanf("%d%d%d", &a[i].d, &a[i].s, &a[i].t);

    int lef = 1, rig = m;
    while(lef <= rig)
	{
        int mid = (lef + rig)>>1;
        if(check(mid)){
            ans = mid;    //如果能满足,就记录一下当前订单数
            lef = mid + 1;
        }
        else rig = mid - 1;
        for(int i = 1; i <= n; ++i)cha[i] = 0;
    }
    if(ans == m)cout<<0<<endl;
    else{
        cout<<-1<<endl;
        //因为当恰好不满足的时候二分退出,而ans记录的是满足的(最后一个)一个订单,
        //所以ans+1就是恰好不满足的那一个订单。 
        cout<<ans+1<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_74183164/article/details/129813836