地域划分问题(利用二进制的相关运算解决)
问题描述:现在有一块长条形的土地,这个土地我们可以看成是由n块小方格连接而成的(这些小方格我们可以将之编号为1到n)。而我们需要将其划分成两个部分,分别种上不同的作物(即作物A和B),划分必须在某两个小方格之间进行,或者在土地的最左端或最右端,若划分在第i块到第i+1块间进行,则划分后,第1至第i块地种A,剩下的地种B。现在有一些专家对土地进行了检测,他们每个人评估了每块土地适合种的作物。请你找到一个合适的划分,使得其与所有专家的评估最吻合,也就是说,你划分到A而专家评估为B的次数和你划分到B而专家评估为A的次数之和最小。
- 分析:该问题实际上就是寻找一种(0 *1*)划分使得该划分能够匹配大多数已有的划分(专家评估)。因为给出的划分形式固定,即前部分为0,后部分为1,所以可以循环匹配测试所有的划分,然后从中找出合适的划分。
-如何解决匹配问题:为了确定你的划分与给定的划分是否一致,直观上应该使用二进制运算。比如给定划分为(00101),而你的划分是(00111),那么只有第3位置(编号0起)不同,而00101^00111=00010,其中1的个数就是划分的差异值,记录这个值,并将该划分对应的所有差异值相加,即为本次划分不能匹配所有专家评估的值,再从中选择使值最小的划分,即为本问题的解。
Java代码
public class Find_min {//寻找一种划分,可以得到最佳的划分位置
public static int[]find_min(int[][]land,int n,int m){
int[]a1=new int[2];
int result=0,divide=0;
int min=0,min_num=0x7fffffff;
a1[0]=0;
a1[1]=0;
if(n<=0||m<=0){
return a1;
}else{
int []b=new int[n];
for(int j=0;j<n;j++){
b[j]=1;
divide=(divide<<1)+b[j];
}
int t = 0;
while (divide>=0) {
for (int i = 0; i < m; i++) {
for (int k = 0; k < n; k++) {
result = (result << 1) + land[i][k];
}
result = result ^ divide;
int num = 0;
while (result > 0) {
result = result & (result - 1);
num++;
}
min += num;
}
++t;
if (min_num > min) {
min_num = min;
//min=0;
a1[0]=t-1;
a1[1]=t;
}
min=0;//此处一定要清零,不能在if条件中清零!
if(divide==0)break;
divide = (divide >> 1);
}
return a1;
}
}
public static void main(String[]args){//测试用例
int a[][]={{0,0,0},{0,0,1},{1,1,1},{1,1,0},{1,0,0}};
int n=3;
int m=5;
int b[]={0,0};
b=find_min(a,n,m);
System.out.print(b[0]);
System.out.print(' ');
System.out.println(b[1]);
}
}
学习笔记
- 二进制运算(与&、或|、非!、异或^、左移<<、右移>>、无符号右移>>)等在许多时候可以对程序性能有一定程度的提升。比如判断奇偶性时,num&0x1比num%2要好,数的乘除2,使用移位运算更加快捷。
- 判断一个整数二进制表示中所含1的个数时,每次n&(n-1)可以把二进制中最后一个1转换成0,所以通过循环即可判断n中所含1的个数了。
- 使用异或运算判断一个数组中仅出现一次的的数字或者两个与其他数字各不相同的数字(其他数字均出现两次或者成对出现),其原理是将数组中所有数字异或运算,根据结合律相同或成对出现的数字异或后等于0,对结果不影响,只有不同的数字会出现在在结果表示中。(具体内容可参考剑指offer例题分析)