算法进阶指南-基本算法-前缀和与差分

1.激光炸弹
题目链接
在这里插入图片描述
题解:这题的解法有点类似前缀和,如果这题是给你一个线段,线段上每个点都有其对应的价值,有一个炸弹能摧毁长度为n的线段,求能摧毁的最大总价值,那么通过前缀和来做很容易解决,而这题就是把一维的前缀和变成了二维的前缀和,炸弹的爆炸范围是一个正方形,我们只需要知道对角上2个点的位置就能确定这个三角形,所已我们用一个二维数组 f[x][y] 来表示0 0 点和x,y点所确定的矩形包含的价值.那么这题也就转化为了一个前缀和问题.现在的问题是如何处理这个二维数组
看如下代码(f[i][j]在处理前储存的是该点的价值)

for(int i = 1; i <= 5001; ++ i)
        {
            for(int j = 1; j <= 5001; ++ j)
            {
                f[i][j] = f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
            }
        }

那么这题也就迎刃而解了

c++代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 5000 + 50;
typedef long long ll;
int f[maxn][maxn];
int main()
{
    int n, r;
    while(scanf("%d%d", &n, &r) != EOF)
    {
        if(r == 0)
        {
            printf("0\n");
            continue;
        }
        int x, y, v;
        memset(f, 0, sizeof(f));
        for(int i = 0; i < n; ++ i)
        {
            scanf("%d%d%d", &x, &y, &v);
            f[x+1][y+1] = v;
        }
        for(int i = 1; i <= 5001; ++ i)
        {
            for(int j = 1; j <= 5001; ++ j)
            {
                f[i][j] = f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
            }
        }
        int ans = 0;
        for(int i = r; i <= 5001; ++ i)
        {
            for(int j = r; j <= 5001; ++ j)
            {
                ans = max(ans, f[i][j]-f[i-r][j]-f[i][j-r]+f[i-r][j-r]);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

java代码:

import java.util.Scanner;

public class Main
{
	static final int N=5001;
	static int[][] s=new int[N+5][N+5];
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int r=sc.nextInt();
		for(int i=0;i<n;i++)
		{
			int x=sc.nextInt();
			int y=sc.nextInt();
			int v=sc.nextInt();
			s[x+1][y+1]=v;
		}
		for(int i=1;i<=N;i++)
		{
			for(int j=1;j<=N;j++)
			{
				s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
			}
		}
		int _max=0;
		for(int i=r;i<=N;i++)
		{
			for(int j=r;j<=N;j++)
			{
				if(s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]>_max)
					_max=s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r];
			}
		}
		System.out.println(_max);
		sc.close();
	}
}


2.IncDec Sequence
题目链接
在这里插入图片描述
题解:首先这题一个重点,就是区间[l,r]的修改操作。因为这道题目的修改操作有一个特性,就是只加一或者只减一,而不是+x,也不是−x,所以说我们并不需要用到高级的数据结构,线段树和树状数组,而只是需要用差分即可。
如何才能保证每个数都一样,只需要保证相邻2数之差为0即可.假设我们将所有相邻2个数的差存在b[n]这个数组里面,b[n]=a[n]-a[n-1];那么此时我们就是想办法把b[n]这个数组变成0.我们看看题目中一个区间加一或减一对b[n]的影响假设让[a,b]段的数加一 则b[b]减一 b[a]加一 ,为了让b数组尽快变成0 ,通过贪心的思想我们让b数组正负配对,正数减一 负数加一,如果正数或负数存在多的 就让它和b[1],因为b[1]是否为0并不影响
c++代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
  vector<int> d;
  d.push_back(0);
  int n, x, y;
  cin >> n;
  cin >> x;
  for (int i = 1; i < n; i++) {
    cin >> y;
    d.push_back(y - x);
    x = y;
  }
  long long pos = 0, neg = 0;
  for (auto x : d) {
    if (x > 0) pos += x;
    else neg -= x;
  }
  cout << min(pos, neg) + abs(pos - neg) << endl;
  cout << abs(pos - neg) + 1;
  return 0;
}

java代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static final int N=100000+10;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n;
        long a[]=new long [N];
        n= sc.nextInt();
        for(int i=1;i<=n;i++)
            a[i]=sc.nextInt();
        long q,p;
        q=0;
        p=0;
        for(int i=n;i>=2;i--)
        {
            long x=a[i]-a[i-1];
            if(x>0)
                q+=x;
            else
                p-=x;

        }
        System.out.println(Math.max(q,p));
        System.out.println(Math.abs(q-p)+1);


    }




}

3.tallest cow
题目链接
在这里插入图片描述
题解:为了使每头牛身高最高,由题意可得 第一头牛和最后一头牛可以拥有题目给出的最高身高,然后其余的牛根据差分关系求得即可,假设a,b能相互看见那么说明a,b中间的牛比ab都要矮,那么cha[a+1]-1 cha[b]+1;
c++代码:

#include <iostream>
#include <cstdio>
#include <cmath>
 
using namespace std;
 
int N, P, H, M;
int a[10005];
bool appear[10005][10005];
 
int main()
{
    cin >> N >> P >> H >> M;
    a[1] = H;
    for (int i = 1, A, B; i <= M; i ++ )
    {
        cin >> A >> B;
        if(A > B) swap(A, B);
        if(!appear[A][B])
        {
            appear[A][B] = 1;
            a[A + 1] --, a[B] ++;
        }
    }
 
    for (int i = 1; i <= N; i ++ )
        a[i] += a[i - 1], cout << a[i] << endl;
    return 0;
}


java代码:

import java.util.*;
public class Main {

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int[] arr = new int[n + 1];
        int p = cin.nextInt();
        int h = cin.nextInt();
        Arrays.fill(arr, h);
        int m = cin.nextInt();
        Set<String> set = new HashSet<String>();
        int[] op = new int[2];
        for (int i = 0; i < m; i++) {
            op[0] = cin.nextInt(); // A
            op[1] = cin.nextInt(); // B
            Arrays.sort(op);
            if (!set.contains(op[0] + "" + op[1])) {
                set.add(op[0] + "" + op[1]);
                for (int c = op[0] + 1; c < op[1]; c++)
                    arr[c]--;
            }
        }
        for (int i = 1; i <= n; i++)
            System.out.println(arr[i]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值