昨天,有很多同学参加微软2014实习生及秋令营技术类职位在线测试,有些还拿了350分,实在是厉害。
第三题是这样的
-
3,1,2 1,2,3,4,5
样例输出
-
1 0
Description
Find a pair in an integer array that swapping them would maximally decrease the inversion count of the array. If such a pair exists, return the new inversion count; otherwise returns the original inversion count.
Definition of Inversion: Let (A[0], A[1] ... A[n]) be a sequence of n numbers. If i < j and A[i] > A[j], then the pair (i, j) is called inversion of A.
Example:
Count(Inversion({3, 1, 2})) = Count({3, 1}, {3, 2}) = 2
InversionCountOfSwap({3, 1, 2})=>
{
InversionCount({1, 3, 2}) = 1 <-- swapping 1 with 3, decreases inversion count by 1
InversionCount({2, 1, 3}) = 1 <-- swapping 2 with 3, decreases inversion count by 1
InversionCount({3, 2, 1}) = 3 <-- swapping 1 with 2 , increases inversion count by 1
}
Input
Input consists of multiple cases, one case per line.Each case consists of a sequence of integers separated by comma.
Output
For each case, print exactly one line with the new inversion count or the original inversion count if it cannot be reduced.
就是给我们一个数组,让我们只交换其中的两个元素一次,使得逆序数降低的最多。
最普通的方法,无非是遍历O(n^2),然后每次计算逆序数,复杂度是O(n^4)
稍微改进一下,计算逆序数利用分治法,修改mergesort,复杂度也还是O(n^3*logn)
再稍微改进一下 , 当我们交换a[i]和a[j]时,对i前面和j后面的逆序数不产生影响,只对i,j中间的产生影响,这样的时间复杂度是O(n^3)
今天,我又尝试了一下贪心算法,就是交换改变距离最大的,那么什么是距离最大呢?
比如我有数组5,3,1,2,4
排序完的话是1,2,3,4,5
5,3的距离是0,这是因为5换到3后,距离排序完的5近了一步,但是3换过去距离排序完的3又远了。
5,1的距离是4,交换后都近了2格
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <time.h>
using namespace std;
int abs(int a,int b)
{
if(a > b)
return b-a;
return a-b;
}
int count1(vector<int>& array, int len)
{
int r = 0;
for(int i = 0;i < len;i++){
for(int j = i+1;j< len;j++){
if(array[i] > array[j])
r++;
}
}
return r;
}
int count2(vector<int>& array, int len)
{
vector<int>dup = array;
sort(dup.begin(),dup.end());
map<int,int>index;
for(int i = 0;i<len;i++)
index[dup[i]] = i;
int r1 = 0,r2 = 0,diff = -1;
for(int i = 0;i < len;i++)
{
for(int j = i+1; j < len;j++)
{
if(array[i] > array[j])
{
int index1 = index[array[i]];
int index2 = index[array[j]];
int differ1 = abs(index1 - i) - abs(index1 - j);
int differ2 = abs(index2 - j) - abs(index2 - i);
if((differ1+differ2) > diff)
{
diff = differ1+differ2;
r1 = i;
r2 = j;
}
}
}
}
int tmp = array[r1];
array[r1] = array[r2];
array[r2] = tmp;
int count = count1(array,len);
return count;
}
// random generator function:
int myrandom (int i) { return std::rand()%i;}
int main(int argv, char** args)
{
srand(time(NULL));
vector<int>arr;
int len = 100;
for(int i = 1;i <= len; i++)
arr.push_back(i);
while(1){
random_shuffle(arr.begin(), arr.end(), myrandom);
//计算min_count
int min_count = count1(arr, len);
for(int i = 0;i < len;i++){
for(int j = i+1;j < len;j++){
if(arr[i] > arr[j]){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
int count = count1(arr,len);
if(min_count > count)
min_count = count;
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
cout << min_count << endl;
//第二种方法
int min_count_2 = count2(arr, len);
cout << min_count_2 <<endl;
if(min_count != min_count_2)break;
}
return 0;
}
这是最原始的方法和贪心算法在一起,当我令数组大小为100,下面是我的输出
2592
2592
2210
2210
2517
2517
2717
2717
2296
2296
2370
2370
2283
2283
2423
2423
2254
2254
2402
2408
昨天同学告诉我他的算法。比如下面是一个数组
6 | 2 | 3 | 1 | 4 | 5 | 10 | 7 | 9 |
怎么样的数字应该往右交换呢?比如在上面2,3是连续的,更应该选3还是选2?请大家仔细想想
想明白了么?从左边遍历,选6,3,10,9 引文6后面是2,6在这一片最大,然后2,3递增,3最大,然后4,5,10,10最大
什么样的数字应该往左交换呢?
那就从右边往左遍历,寻找区域最小的,也就是7,1,2,6
好了,现在我们有2个集合了,最后的可能性就在集合A选一个集合B选一个
再进行适当的剪枝就能缩小数据大小