二分、前缀和(激光炸弹)几道例题的详细讲解,帮助理解相关知识点

二分和前缀和

激光炸弹

#include <iostream>
using namespace std;
int map[1001][1001];
int main()
{
	int n ,r;
	cin >> n >> r;
	int ra, rb;
	ra = rb = r;
	
	for (int i = 0; i < n; i++)
	{
		int x, y,w;
		cin >> x >> y>>w;
		x++;//目的是为了防止计算的时候越界。
		y++;
		map[x][y] += w;//因为值可以叠加所以是+=;
		ra = max(x, ra);
		rb = max(y, rb);
	}
	for (int i = 1; i <= ra; i++)
	{
		for (int j = 1; j <= rb; j++)
		{
			map[i][j] += map[i - 1][j] + map[i][j - 1] - map[i - 1][j - 1];
            //这里的作用是先将地图的值全部转换为前缀和的值。注意一下这个for循环语句里的形式和下一个for循环语句里面不一样的地方。
		}
	}
	int ans = 0;
	for (int i = r; i <= ra; i++)
	{
		for (int j = r; j <= rb; j++)
		{
			/*map[i][j] -= map[i - r][j] - map[i][j - r] + map[i - r][j - r];
			ans = max(ans, map[i][j]);*/
            //这种形式是错误的,因为这样会修改地图里面map[i][j]的值。  正确的写法是下面这钟。
            ans=max(ans,map[i][j]-map[i-r][j]-map[i][j-r])+map[i-r][j-r];//这样就没有改变地图的值。
		}
		
	}
	cout << ans;
	system("pause");
	return 0;
}

数的三次方根(利用二分)

#include<iostream>
#include <iomanip>
using namespace std;
int main()
{
    double n;
    cin>>n;
    double l=-10000.0,r=10000.0;
    for(int i=0;i<100;i++)//一百次循环肯定达标了
    {
        double mid=(l+r)/2.0;
        if(mid*mid*mid>n) r=mid;
        else l=mid;
    }
   cout << setiosflags(ios::fixed) << setprecision(6) << l << '\n';
    return 0;
}

注:这里还有一钟方式,不过不能AC,没搞懂为什么不能AC。。,这里我也记录一下这种方式,虽然思想一样。

#include<iostream>
#include <iomanip>
#define esp 1e-10
using namespace std;
double n;
int fun(double x)//注意一下这里的参数必须是double类型
{
    return x * x * x > n ? 1 : 0;
}
int main()
{

    cin >> n;
    double l, r;
    l = 0, r = n;
    if (n < 0) swap(l, r);
    while (r - l >esp) //当没有达到这个精度时一直循环
    {
        double mid = (r + l) / 2;
        if (fun(mid))r = mid;
        else l = mid;
    }
    cout << setiosflags(ios::fixed) << setprecision(6) << l;
}

子矩阵的和

#include <iostream>
#define  N 10001
using namespace std;
int a[N][N];
int ans;
int main()
{
	int m, n,h;
	cin >> n >> m>>h;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
		}
	}
	while (h--)
	{
		int x1, x2, y1, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		for (int i = x1; i <= x2;i++)
		{
			for (int j = y1; j <= y2; j++)
			{
				ans += a[i][j];
			}
		}
		cout << ans << '\n';
		ans = 0;
	}

	system("pause");
	return 0;
}

分巧克力

使用二分的方法减少循环次数。

要使巧克力面积最大化,所以要从边最长的数字开始检测。

分别求宽和高对最大边长取余,宽和高取余后的乘积就是那一块大巧克力能分出来的小巧克力的块数。

#include <iostream>
using namespace std;
struct chocolet//使用结构体来对巧克力进行封装。
{
	int width, height;
	int max;
}a[1000001];
int main()
{
	int n, k;
	int maxn = 0x100001;
	cin >> n >> k;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i].width >> a[i].height;
		a[i].max = max(a[i].width, a[i].height);//用来记录单个巧克力的最长边
		maxn = max(maxn, a[i].max);//记录所有巧克力的最长边。
	}
	int l = 0, r = maxn,mid,ans=0;
	while (l <=r)
	{
		int cnt = 0;
		mid = (l + r) >> 1;
		for (int i = 0; i < n; i++)
		{
			int h = a[i].height / mid;
			int j = a[i].width / mid;
			cnt += h * j;//当前巧克力能分出来mid边长大小的巧克力的个数。
		}
		if (cnt >= k)
		{
			l = mid + 1;//根据循环条件来决定的
			ans = mid;//这步很重要,记录满足条件的最大边长值-
		}
		else
			r = mid - 1;
	}
	cout << ans;
	system("pause");
	return 0;
}

K倍区间

有一种抽屉的感觉

原理就是对每一个a[]数组的值取k的余,如果余相同,则说明这两个数的区间是满足条件的。

问题就转变到计算每个不同的余出现的个数,第一个出现的不算,从第二次出现就开始计算,具体实现代码是第19行

举例:如a[i]

#include <iostream>
using namespace std;
int a[10000010];
int bk[10000010];
int main()
{
	int n, k;
	cin >> n >> k;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	int sum = 0;
	for (int i = 1; i < n; i++)
	{
		a[i] = (a[i] + a[i - 1]) % k;//对数组a[]里面的每一个数都进行取余操作。
	}
	a[0] %= k;
	for (int i = 0; i < n; i++)
	{
		sum += (bk[a[i]]++);//重点:这里是后置++
        //抽屉原理,bk[]数组的每一个数都是0,如果a[1]=1(a[1]已经是取了余的)则b[1]++;此时b[1]=1;但如果b[1]最终的值也1的话,就不用加进sum里面,因为这说明a[]数组里面的数取余等于1的数只有一个,出现两次sum就加1,出现3次sum+2,一次类推,记住这里是后置++,
	}
	cout << sum + bk[0] << endl;
    //这里要记得加上bk[0]的个数,因为a[]取余=0的数也满足K倍区间的条件。

	system("pause");
	return 0;
}

四平方和、

我做题的思路就是先定义a,b,c,d如果在接下来的过程中没有影响到他们的值就默认输出0。

先用sqrt(n)来求到有一个大致的范围,然后求差值(n-pow(sqrt(n),2),这样依次类推,过程还是有一点复杂。

#include <iostream>
#include <cmath>
using namespace std;
int a, b, c, d;
int main()
{
	long long int n; int flag = 1;
	cin >> n;
	 d = sqrt(n);
	 if ((n - pow(d, 2)) != 1&& (n - pow(d, 2)) != 0)
		 c = sqrt(n - pow(d, 2));
	 else
	 {
		 if ((n - pow(d, 2)) == 1)
			 c = 1;
		 cout << a <<" "<< b <<" " << c<<" " << d;
		 return 0;
	 }
	if ((n - pow(c, 2)- pow(d, 2)) != 1 && (n - pow(c, 2)- pow(d, 2)) != 0)
		b = sqrt(n - pow(c, 2) - pow(d, 2));
	else
	{
		if ((n - pow(c, 2) - pow(d, 2)) == 1)
			b = 1;
		cout << a << " " << b << " " << c << " " << d;
		return 0;
	}
	if ((n - pow(b, 2) - pow(c, 2) - pow(d, 2)) != 1 && (n - pow(b, 2) - pow(c, 2) - pow(d, 2)) != 0)
	{
		a = sqrt(n - pow(b, 2) - pow(c, 2) - pow(d, 2));
		cout << a << " " << b << " " << c << " " << d;
		system("pause");
		return 0;
	}
	else
	{
		if ((n - pow(b, 2) - pow(c, 2) - pow(d, 2)) == 1)
			a = 1;
		cout << a << " " << b << " " << c << " " << d;
		return 0;
	}
	

	system("pause");
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_51721904/article/details/121340512