Arrays 使数组对和相等的最小操作数

Arrays 使数组对和相等的最小操作数,arrays,algorithm,Arrays,Algorithm,您将得到一个长度为偶数的整数列表。考虑一个操作,您在NUM中选择任何数字,并用一个值(1,max(NUMS)”更新它。返回所需的操作数,使每个i的nums[i]+nums[n-1-i]等于相同的数。这个问题可以迎刃而解 注意:n是数组的大小,max(nums)是nums中的最大元素 例如:nums=[1,5,4,5,9,3]预期的操作是2 说明:最大nums是9,因此我可以将nums的任何元素更改为[1,9]之间的任何数字,这需要一次操作 在索引0处选择1并将其更改为6 在索引4处选择9并将其

您将得到一个长度为偶数的整数列表。考虑一个操作,您在NUM中选择任何数字,并用一个值(1,max(NUMS)”更新它。返回所需的操作数,使每个i的nums[i]+nums[n-1-i]等于相同的数。这个问题可以迎刃而解

注意:n是数组的大小,max(nums)是nums中的最大元素

例如:nums=[1,5,4,5,9,3]预期的操作是2

说明:最大nums是9,因此我可以将nums的任何元素更改为[1,9]之间的任何数字,这需要一次操作

  • 在索引0处选择1并将其更改为6
  • 在索引4处选择9并将其更改为4
现在这使得nums[0]+nums[5]=nums[1]+nums[4]=nums[2]+nums[3]=9。我们已经更改了2个数字,这花费了我们2次操作,这是该输入的最低成本


我使用的方法是求和的中值,然后用它来贪婪地求运算的次数。 让我们根据给定的条件求数组的所有和

  • 总和可以用nums[i]+nums[n-1-i]计算

  • 设i=0,nums[0]+nums[6-1-0]=4

  • i=1,nums[1]+nums[6-1-1]=14

  • i=2,nums[2]+nums[6-1-2]=9

将这些总和存储在数组中并对其排序。 排序后的总和=[4,9,14]。现在求和的中值,它是9,因为它是中间元素

现在我用这个中位数来平衡和,我们可以找到运算的次数。我还添加了用于计算操作数的代码

int operations = 0;
for(int i=0; i<nums.size()/2; i++) {
    if(nums[i] + nums[nums.size()-1-i] == mid)
        continue;
        
    if(nums[i] + nums[nums.size()-1-i] > mid) {
        if(nums[i] + 1 <= mid || 1 + nums[nums.size()-1-i] <= mid) {
            operations++;
        } else {
            operations += 2;
        }
    } else if (maxnums + nums[nums.size()-1-i] >= mid || nums[i] + maxnums >= mid) {
        operations++;
    } else {
        operations += 2;
    }
}
int运算=0;
对于(int i=0;i mid){
如果(nums[i]+1=mid){
操作++;
}否则{
操作+=2;
}
}
本例的总操作数为2,这是正确的

这里的问题是,在某些情况下,选择中值会给出错误的结果。例如,nums=[10,7,2,9,4,1,7,3,10,8]需要5次运算,但如果选择了中位数(16),我的代码会给出6次运算

选择中位数不是最理想的方法吗?有人能提供更好的方法吗?

(收到更多信息后更新)

最佳和必须是以下之一:

  • 一对的总和->因为你可以保留那对的两个数字
  • 一对的最小值+1->因为它是可能的最小和,您只需更改该对的1个数字即可
  • 一对的最大值+最大总值->因为它是最大可能的总和,您只需更改该对的1个数字即可
因此,存在N阶可能的和

可通过各种方式计算此最佳和的操作总数

O(N²)是非常琐碎的。如果您想确认其他解决方案是否有效,您可以非常轻松地实现它

使其O(N日志N)

  • 获取所有可能的最优和O(N)
  • 对于每个可能的和,您可以计算具有该精确和的对的数目,因此不需要任何操作O(N)
  • 对于所有其他对,您只需要知道它是否需要1或2次运算才能得到该和。如果一对中的最小值太大而无法达到可能的最小值,或者一对中的最大值太小而无法达到可能的最大值,则为2。许多数据结构可用于此(位、树等)。我只是使用了一个排序列表并应用了二进制搜索(虽然没有经过详尽的测试)O(N日志N)
java中的示例解决方案:

int[] nums = new int[] {10, 7, 2, 9, 4, 1, 7, 3, 10, 8};
// preprocess pairs: O(N)
int min = 1
    , max = nums[0];
List<Integer> minList = new ArrayList<>();
List<Integer> maxList = new ArrayList<>();
Map<Integer, Integer> occ = new HashMap<>();
for (int i=0;i<nums.length/2;i++) {
    int curMin = Math.min(nums[i], nums[nums.length-1-i]);
    int curMax = Math.max(nums[i], nums[nums.length-1-i]);
    min = Math.min(min, curMin);
    max = Math.max(max, curMax);
    minList.add(curMin);
    maxList.add(curMax);
    // create all pair sums
    int pairSum = nums[i] + nums[nums.length-1-i];
    int currentOccurences = occ.getOrDefault(pairSum, 0);
    occ.put(pairSum, currentOccurences + 1);
}
// sorting 0(N log N)
Collections.sort(minList);
Collections.sort(maxList);
// border cases 
for (int a : minList) {
    occ.putIfAbsent(a + max, 0);
}
for (int a : maxList) {
    occ.putIfAbsent(a + min, 0);
}

// loop over all condidates O(N log N)
int best = (nums.length-2);
int med = max + min;
for (Map.Entry<Integer, Integer> entry : occ.entrySet()) {
    int sum = entry.getKey();
    int count = entry.getValue();
    int requiredChanges = (nums.length / 2) - count;
    if (sum > med) {
        // border case where max of pair is too small to be changed to pair of sum
        requiredChanges += countSmaller(maxList, sum - max);
    } else if (sum < med) {
        // border case where having a min of pair is too big to be changed to pair of sum
        requiredChanges += countGreater(minList, sum - min);
    }
    System.out.println(sum + " -> " + requiredChanges);
    best = Math.min(best, requiredChanges);
}
System.out.println("Result: " + best);
}

// O(log N)
private static int countGreater(List<Integer> list, int key) {
 int low=0, high=list.size();
 while(low < high) {
     int mid = (low + high) / 2;
     if (list.get(mid) <= key) {
        low = mid + 1;
    } else {
        high = mid;
    }
 }
 return list.size() - low;
}

// O(log N)
private static int countSmaller(List<Integer> list, int key) {
 int low=0, high=list.size();
 while(low < high) {
     int mid = (low + high) / 2;
     if (list.get(mid) < key) {
        low = mid + 1;
    } else {
        high = mid;
    }
 }
 return low;
}
int[]nums=newint[]{10,7,2,9,4,1,7,3,10,8};
//预处理对:O(N)
int最小值=1
,max=nums[0];
List minList=new ArrayList();
List maxList=new ArrayList();
Map occ=newhashmap();
对于(int i=0;i med){
//边界情况下,对的最大值太小,无法更改为和的对
requiredChanges+=countSmaller(最大值列表,总和-最大值);
}否则如果(总和<平均值){
//边界情况下,对的最小值太大,无法更改为和的对
requiredChanges+=CountMorger(最小列表,总和-最小值);
}
系统输出打印项次(总和+“->”+所需更改);
best=Math.min(最佳,所需更改);
}
System.out.println(“结果:+最佳);
}
//O(对数N)
私有静态整数更大(列表,整数键){
int low=0,high=list.size();
while(低<高){
int mid=(低+高)/2;

如果(list.get(mid)我认为以下方法应该有效:

  • 迭代数对
  • 对于每一对,计算该对的和,以及仅更改其中一个值即可实现的最小和最大和
  • 当开始一个新的“区域”需要较少的更改时,用-1更新字典/地图;当该区域结束时,用+1更新字典/地图
  • 迭代该字典中的边界并更新所需的总更改,以找到需要最少更新的总和
Python中的示例代码,将
9
作为示例的最佳总和,需要
5
更改

从集合导入defaultdict
nums=[10,7,2,9,4,1,7,3,10,8]
m=最大值(nums)
pairs=[(nums[i],nums[-1-i])表示范围内的i(len(nums)//2)]
打印(双)
分数=defaultdict(int)
对于地图中的a、b(已排序,成对):
低=a+1
高=m+b
分数[低]-=1
分数[a+b]-=1
分数[a+b+1]+=1
分数[高+1]+=1
打印(已排序(score.items())
cur=best=len(nums)
num=无
对于排序中的i(分数):
cur+=分数[i]
打印(i,cur)
如果cur<最佳值:
最佳,num=cur,i
打印(最佳,num)
此操作的总复杂度应为O(nlogn),需要O(n)来创建dict