1. 题目描述:
给定一个数列,每一次移动可以将数列的某个数移动到某个位置,移动多次后,形成新的数列。定义数列中相邻两两之间的差的绝对值为“移动距离”,定义所有移动距离的总和为“总移动距离”。希望计算出最少的移动次数,使得新数列的“总移动距离”最小。例如原数列为[4,2,7, 6],总移动距离为2+5+1=8.将6移动到7之前,会变成[4,2,6,7],总移动距离为2+4+1=7。
需要编写一个函数,输入为一个int数组表示数列内容,输出为一个int数字,表示最小移动次数。
输入
第一行输入为数组大小,然后依次输入数组元素,如数组[4,2,7,6]。
输出
总移动距离最小的数列之一为[2,4,6,7]
最少移动次数为:2
2. 思路分析
题目定义的“移动距离”为相邻两数之间差的绝对值,要使“总移动距离”最小,移动之后的数据应该为一个有序数列(升序或者降序),这样得到“总移动距离”最小。
最小移动次数:数组长度减去原始数列中最长升序子序列的长度,即得到最小移动次数。
例如:
原始数组 1 6 3 4 8 9 7 2 5 11
最长升序子序列为 1 3 4 8 9 11,长度为6,所以最小移动次数为10 - 6 = 4。
所以题目最终转化为,求给定数列中最长升序子序列的长度,当然也要计算最长降序子序列的长度,然后比较降序和升序的相应的移动次数,得到两者的较小值,即为本题的答案。
3. 最长升序子序列
最简单的方法:动态规划
状态设计:F[i]代表以A[i]结尾的LIS的长度
状态转移:F[i]=max{F[j]+1}(1<=j< i,A[j]< A[i])
边界处理:F[i]=1(1<=i<=n)
时间复杂度:O(n^2)
#include
// 求最长升序子序列的长度
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int a[] = {1,6,4,3,7,8,9,2,5,11};
int b[] = {1,6,3,4,8,9,7,2,5,11};
int dp[10] = {0};
int maxn = 0;
for(int i = 0;i < 9;i++)
{
for(int j = 0;j < i;j++)
{
if(a[j] < a[i]) // a[j] > a[i] 降序
{
maxn = max(dp[j], maxn);
printf("%d, %d\n", a[i], maxn);
}
}
dp[i] = maxn + 1;
}
maxn = dp[0];
for(int i = 0; i < 9; i++)
{
maxn = max(dp[i], maxn);
}
printf("%d", maxn);
getchar();
}
参考
后记
本人水平有限,本文如有错误,欢迎指正。