【题目描述】
给定一个长度为 n 的数列A1,A2,... , An 和一个非负整数 x。
给定 m 次查询, 每次询问能否从某个区间 [l, r] 中选择两个数使得他们的异或等于 x。
【输入格式】
输入第一行包含三个整数n,m,x。
第二行包含n个整数A1,A2,...,An。
接下来m行,每行两个整数l,r表示询问区间[l, r]。
20%的测试数据:1≤n,m≤100;
40%的测试数据:1≤n,m≤1000;
100%的测试数据:1≤n,m≤100000,0≤x,Ai<2^20,1≤l≤r≤n;
【输出格式】
对于每个询问, 如果该区间内存在两个数的异或为 x 则输出yes, 否则输出no。
输入样例:
4 4 1
1 2 3 4
1 4
1 2
2 3
3 3
输出样例:
yes
no
yes
no
解题思路:首先对JAVA中的异或符号的计算原理进行分析:
例如对于1^2进行分析:1的二进制码为:1。2的二进制码为10,异或是对于同位上的数字相同为0,不同为1,以下是计算过程:
1.......1
^
2......10
=......11
由于二进制(11)对应的10进制结果为(3),所以1^2=3。
除此之外异或还有这些性质,对优化方法解决本题是必不可少的:
例如a^b=c,那么有b^a=c,b^c=a,b^a=c。
那么再回到本题:很容以想到的就是直接利用所给区间去挨个寻找是否存在两个数的异或等于x。这种解法为暴力枚举法,代码如下:
解法一
import java.util.*;
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 = sc.nextInt();
int a[] = new int [n+1];
for(int i = 1;i <= n;i++) {
a[i] = sc.nextInt();
}
for(int i = 0;i < m;i++) {
int l = sc.nextInt();
int r = sc.nextInt();
myfer(a,l,r,x);
}
}
public static void myfer(int a[],int l ,int r,int x)
{
for(int i = l ;i <= r;i++) {
for(int j = l+1;j <= r;j++) {
if((a[i]^a[j])==x)
{
System.out.println("yes");
return ;
}
}
}
System.out.println("no");
}
}
不出意外地话就出意外了,由于数据量庞大,所以这种解法只能对一半的数据,剩下的就超时了。
值得一提的是让计算机去解决一道题本质上就是穷举,要想让代码效率更高,就看能否尽可能地去去掉重复的步骤 。
由于上述方法在每次给定区间了l,r时都会重新查询,这其中必然有很多区间查重复了例如:若
区间 2,4存在异或等于x的数那么区间1,4那也一定存在异或等于x的数,理应区间1,4是不用再计算了,但上述方法依旧进行计算,这就是重复的来源,那么解决重复的方向也就找到了。
如果我们能把2,4的结果保留下来,那么1,4就不必计算了:
1.这里我们需要设定一个dep[i]=l数组,其中i代表数列中的第i个元素,其中在这个l,i区间内除非没有符合条件的一对数,否者有且仅有一对数使得它们的异或等于x,这样的l,i区间的特点是范围很小,任何区间只要包含了l,i那就符合条件。
2.再需要设立一个map[a] = i数组,其中a代表着数列中的某个元素a,i代表这个元素的确切位置.
3核心算法:dep[a的位置] = max(dep[a前一个元素],map[a^x]);这是因为前一个元素可能有符合条件的对应元素,本元素可能没有符合条件的对应元素,也可能有,那就比谁的大就储存哪个喽。就这样层层递进,这就是动态规划。
方法二
import java.util.*;
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 = sc.nextInt();
int a = 0;
int dep[] = new int [n+1];//储存数列某个位置,至少存在一对数异或等于x的最大左下标。
int map[] = new int [1<<20];//储存数列中任意一个元素的位置。大小为2^20
for(int i = 1;i <= n;i++) {
a = sc.nextInt();
dep[i] = Math.max(dep[i-1], map[a^x]);//数列前一个元素也可能有满足条件的下标,取最大即可。
map[a] = i;//写在最后是防止有a^a=x,的可能出现。
//如果map[a^x]>a,由于还没有输入过a^x,所以map[a^x]实际上是默认值0
}
for(int j = 0;j < m;j++) {
int l = sc.nextInt();
int r = sc.nextInt();
System.out.println(dep[r] >= l?"yes":"no");
}
}
}
别看上述代码少,浓缩都是精华。
蓝桥杯题目特点:代码量少,但重思维。