如果1 2 2 2 3 4 5,则答案为5,因为需要将2更改为3,然后将2更改为4,将3更改为5,将4更改为6,将5更改为7。 约束条件: 数组中的元素数量<= 10^6
每个元素<= 10^9
请问有人能给我一个算法吗?
详细问题及样本测试用例链接 https://www.hackerearth.com/problem/algorithm/find-path/
由于这是一个极小/极大化问题,听起来像动态规划,但我不确定。
由于这是一个极小/极大化问题,听起来像动态规划,但我不确定。
这与标准的最长上升子序列问题非常接近,可以在O(nlogn)内解决。
如果您可以将数字更改为小数,则答案将是相同的。 (最小更改次数=字符串长度-最长递增子序列的长度)
但是,由于您需要介于不同整数值之间,因此必须稍微修改标准算法。
考虑一下如果通过x[i]=x[i]-i来更改数组会发生什么。
现在,您需要通过进行最小数量的更改来修改此更改后的数组,使每个元素增加或保持不变。
现在,您可以在该数组中搜索最长的非递减子序列,并且这将告诉您可以保持多少个元素不变。
但是,这可能仍然使用负整数。
将算法修改为仅使用正数的一种简单方法是在数组开头添加许多数字。
例如,将1、2、9、10、3、15更改为-5、-4、-3、-2、-1、1、2、9、10、3、15
然后,您可以确信最优答案永远不会决定使1变为负数,因为这将花费太多时间使所有负数更小。
(您还可以修改最长递增子序列算法以具有附加约束条件,但在面试情况下正确编写可能更难。)
按照初始示例进行操作:
原始序列
1,2,9,10,3,15
在开头添加虚拟元素
-5,-4,-3,-2,-1,1,2,9,10,3,15
从数组中减去位置
-5,-5,-5,-5,-5,-4,-4,2,2,-6,5
找到最长的非递减序列
-5,-5,-5,-5,-5,-4,-4,2,2,*,5
所以答案是改变一个数字。
原始序列
1,2,2,2,3,4,5
在开头添加虚拟元素
-5,-4,-3,-2,-1,1,2,2,2,3,4,5
从数组中减去位置
-5,-5,-5,-5,-5,-4,-4,-5,-6,-6,-6,-6
找到最长的非递减序列
-5,-5,-5,-5,-5,-4,-4,*,*,*,*,*
所以答案是改变5个数字。
50,51,50,50,52,53,57
中是不起作用的,因为你的方法总是会改变数字的下一个位置,而且永远不会回退,因为你在开头加了一个递增的数组。 - Eugen Halca将 最长递增子序列
的修改以适应在两个连续递增元素之间挤压整数值,可以确保我们在适当的位置具有整数值。
修改后的 LIS 代码:
int convert(vector<int> v){
vector<int> lis(v.size(), 1);
for(int i = 1; i < v.size(); i++){
for(int j = 0; j < i; j++){
if(v[i] > v[j] && lis[i] < lis[j]+1 && (i-j) <= (v[i]-v[j]))
lis[i] = lis[j]+1;
}
}
int max_lis = 0;
for(int i = 0; i < v.size(); i++)
max_lis = max(max_lis, lis[i]);
int ans = v.size() - max_lis;
return ans;
}
O(nlogn) 运行时间
import java.util.ArrayList;
import java.util.Scanner;
public class Modify_The_Sequence {
private static int n;
private static int[] a;
private static ArrayList<Integer> b;
private static ArrayList<Integer> c;
public static void main(String args[]) {
Scanner d = new Scanner(System.in);
n = d.nextInt();
b = new ArrayList<>();
c = new ArrayList<>();
for (int i = 0; i < n; i++) {
int x = d.nextInt();
if (x - (i + 1) >= 0) {
b.add(x - (i + 1));
}
}
/*
* b.clear(); b.add(1); b.add(7); b.add(10); b.add(2); b.add(3);
*/
// System.out.println(b);
c.add(b.get(0));
solve();
System.out.println(n - c.size());
// System.out.println(c);
}
private static void solve() {
// TODO Auto-generated method stub
for (int i = 1; i < b.size(); i++) {
binaerysearch(0, c.size() - 1, i);
}
}
private static void binaerysearch(int i, int j, int k) {
// TODO Auto-generated method stub
int p = (i + j) / 2;
if (i == j) {
if (c.get(i) <= b.get(k)) {
if (i == c.size() - 1)
c.add(b.get(k));
else {
c.add(i + 1, b.get(k));
c.remove(i + 2);
}
} else if (c.get(i) > b.get(k)) {
c.add(i, b.get(k));
c.remove(i + 1);
}
} else {
if (c.get(p) > b.get(k)) {
if (p - 1 > i)
binaerysearch(i, p - 1, k);
else
binaerysearch(i, i, k);
} else if (c.get(p) <= b.get(k)) {
if (p + 1 < j)
binaerysearch(p + 1, j, k);
else
binaerysearch(j, j, k);
}
}
}
}
受到@Peter de Rivaz的启发,
我发现这个问题的关键条件是:num[i]-i >= num[j]-j >= 0 (i > j)。
以下Java代码(O(NlgN))是标准最长上升子序列算法的轻微修改,我们在此跳过所有num[i],其中num[i]-i < 0。
static int resolve(int... nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int right = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] >= i) {
int realNum = nums[i] - i;
if (realNum >= dp[right]) {
right++;
dp[right] = realNum;
} else {
dp[binarySearch(dp, 0, right, realNum)] = realNum;
}
}
}
return nums.length - (right + 1);
}
static int binarySearch(int[] nums, int left, int right, int key) {
while (left <= right) {
int mid = (left + right) >>> 1;
if (nums[mid] > key) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
O(n) 运行时间:
def min_changes(li):
change, check = 0, 0
li_len = len(li)
if li_len in [0,1]:
return change
else:
check = li[0]
for i in range(1, len(li)):
if check >= li[i]:
li[i] = check +1
change += 1
check += 1
else:
check = li[i]
return change
使用Python实现时间复杂度为O(n)、空间复杂度为O(1)的解决方案
arr = [1,2,2,2,3,4,5]
count = 0
for i in range(len(arr)-1):
if(arr[i]>=arr[i+1]):
arr[i+1] = arr[i]+1
count += 1
print(count)