专题训练前缀和与差分,尺取,位运算
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);
}
}
加油!
感谢!
努力!
以上是关于专题训练前缀和与差分,尺取,位运算的主要内容,如果未能解决你的问题,请参考以下文章