专题训练前缀和与差分,尺取,位运算

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题训练前缀和与差分,尺取,位运算相关的知识,希望对你有一定的参考价值。

取石子

在这里插入图片描述
输入输出样例

输入:

1
1

输出

Alice

算法分析:

奇偶运算

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long all = 0;
        while (n-- > 0) {
            all += sc.nextInt();
        }
        if ((all & 1) == 1)
            System.out.println("Alice");
        else
            System.out.println("Bob");
    }
}

光骓者的荣耀

在这里插入图片描述
输入输出样例

输入:

4 0
1 2 3

输出

6

在这里插入图片描述

算法分析

考虑线性复杂度,使用一维前缀和。

前缀和推导式:dp[i]:表示前i个数的和,从[i-j]的区间和=dp[j]-dp[i-1]
因此求出传送k的最大节省时间,最后总消耗时间-k范围的区间和,即是最小值。

特殊情况:

k <= 0:无法传送,直接dp[n-1]
k >= n-1:一步到位,0

import java.io.*;

public class Main {
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) {
        int n = nextInt();
        int k = nextInt();
        long[] d = new long[n];
        d[0] = 0;
        for (int i = 1; i < n; i++) {
            d[i] = (d[i - 1] + nextLong());
        }
        long max = 0;
        if (k <= 0)
            System.out.println(d[n-1]);
        else if (k >= n-1) {
            System.out.println(0);
        } else {
            for (int i = 1; i < n; i++) {
                if (i+k < n)  // 1 2 3  1 3 6
                    max = Math.max(max, d[i+k-1] - d[i-1]);
                else
                    max = Math.max(max, d[n-1]-d[i-1]);
            }
            System.out.println(d[n-1] - max);
        }
    }
    static int nextInt() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (int)in.nval;
    }
    static long nextLong() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (long)in.nval;
    }
}

激光炸弹

在这里插入图片描述
输入输出样例

输入:

2 1
0 0 1
1 1 1

输出:

1

算法分析

二维前缀和,求区域最大值,需要知道二维前缀和的推导式:dp[i][j] = (dp[i][j-1]+dp[i-1][j]+dp[i][j]-dp[i-1][j-1]);
区域和的推导式:dp[i][j]-dp[i-m][j]-dp[i][j-m]+dp[i-m][j-m]

注意:

数组开大点,下标从m开始,小于m无需考虑。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main {
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static void main(String[] args) {
        int n = nextInt();
        int m = nextInt();
        int[][] dp = new int[5010][5010];
        while (n-- > 0) {
            dp[nextInt()+1][nextInt()+1] = nextInt();
        }
        for (int i = 1; i < 5010; i++) {
            for (int j = 1; j < 5010; j++) {
                dp[i][j] = (dp[i][j-1]+dp[i-1][j]+dp[i][j]-dp[i-1][j-1]);
            }
        }
        int max = 0;
        for (int i = m; i < 5010; i++) {
            for (int j = m; j < 5010; j++) {
                max = Math.max(dp[i][j]-dp[i-m][j]-dp[i][j-m]+dp[i-m][j-m], max);
            }
        }
        System.out.println(max);
    }
    static int nextInt() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (int)in.nval;
    }
}

语文成绩

在这里插入图片描述
输入输出样例

输入

3 2
1 1 1
1 2 1
2 3 1

输出:

2

算法分析:

差分经典问题:首先要是差分的含义,dp[i]:表示x[i]-x[i-1]
在区间[i,j]加减数值x,只需dp[i]+=x,dp[j+1]-=x;
某一位置i的值=dp[1]+…+dp[i].

import java.io.*;

public class Main {
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) {
        int n = nextInt();
        int p = nextInt();
        int[] x = new int[n+1];
        int[] d = new int[n+1];
        for (int i = 1; i <= n; i++) {
            x[i] = nextInt();
            d[i] = x[i]-x[i-1];
        }
        while (p-- > 0) {
            int a = nextInt();
            int b = nextInt();
            int z = nextInt();
            d[a] += z;
            if (b+1 < n)
                d[b+1] -= z;
        }
        int min = 101;
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += d[i];
            if (sum < min)
                min = sum;
        }
        System.out.println(min);
    }
    static int nextInt() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (int)in.nval;
    }
}

逛画展

在这里插入图片描述
输入输出样例

输入:

12 5
2 5 3 1 3 2 4 1 1 5 4 3

输出:

2 7

算法分析:

双指针,滑动数组,定义左右指针 l,r.
dp[x]:统计编号x的画出现的次数(可以用Set集合替换)
y:表示l-r中不同画的数量。
1):首先当l-r区间中不同画的数量 != m 的画就让 r 右移。
2):当区间中有m个不同画后,更新区间最小范围:r-l.更新a,b
3):此时够m个数了,因此左指针右移,dp[x[l]–.如果后面有x[l]的话,无需执行 y–。
移动左指针相当于缩小区间范围。因为l-r区间中,可能仍然包含x[l]对应的画。如果后面包含x[l],我们直接pop()(模拟队列)。我们移动 l++,相当于缩小区间范围,更靠近答案。

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] x = new int[n+10];
        for (int i = 1; i <= n; i++) {
            x[i] = sc.nextInt();
        }
        int[] d = new int[n+10];
        int l = 1, r = 0, a = 1, b = 1, min = n, y = 0;
        while (l <= n && r <= n) {
            while (y < m) {
                d[x[++r]]++;
                if (d[x[r]] == 1)
                    y++;
            }
            if (min > r-l  && y == m) {
                min = r-l;
                a = l;
                b = r;
            }
            d[x[l]]--;
            if (d[x[l]] == 0)
                y--;
            l++;
        }
        System.out.println(a+" "+b);
    }
}

找筷子

在这里插入图片描述
输入输出样例

输入:

9
2 2 1 3 3 3 2 3 1

输出:

2

算法分析:

位运算,根据性质 x ^ x = 0,x ^ 0 = x。因此直接将所有数异或即可,剩下就是落单的数。

Java内存超限o(╥﹏╥)o

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define long long ll

int main(){
	int n,x;
	cin>>n;
	int res = 0;
	while (n-- > 0) {
		scanf("%d",&x);
		res ^= x;
	}
	cout<<res<<endl;
	return 0;    
}

开灯

在这里插入图片描述
输入输出样例

输入

3
1.618034 13
2.618034 7
1.000000 21

输出

20

算法分析:

暴力遍历,注意输入用double,有一个样例浮点数大,float过不了。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] x = new int[2000001];
        int index = 0;
        while (n-- > 0) {
            double a = sc.nextDouble();
            int t = sc.nextInt();
            for (int i = 1; i <= t; i++) {
                index = (int)(a*i);
                x[index] ^= 1;
            }
        }
        for (int i = 1; i < 2000001; i++) {
            if (x[i] == 1) {
                index = i;
                break;
            }
        }
        System.out.println(index);
    }
}

加油!

感谢!

努力!

以上是关于专题训练前缀和与差分,尺取,位运算的主要内容,如果未能解决你的问题,请参考以下文章

第六章 基础算法

前缀和与差分java模板代码

前缀和与差分数组

前缀和与差分

前缀和与差分

基础算法 --- 前缀和与差分