题目描述
有n个数,你现在需要将这n个数按升序排列。排序过程中每次交换两个数字需要花费代价为 w i + w j w_i+w_j wi+wj。现在请你写一个程序,要求花费最小的代价将其排序。
Input
第一行为一个正整数n,第二行为n个数 w i ( i = 0 , 1 , ⋅ ⋅ ⋅ n − 1 ) w_i(i=0,1,···n-1) wi(i=0,1,⋅⋅⋅n−1)
Output
打印最小代价
Sample Input
5
1 5 3 4 2
Sample Output
7
实现思路
这题还是比较考验思维能力的。对于一个乱序的数组彼此之间交换可以形成一个闭环的圆。这里举 4 , 2 , 3 , 7 , 1 , 6 , 5 4,2,3,7,1,6,5 4,2,3,7,1,6,5的例子,如图所示。
所以我们有以下流程:
- 长度为1的圆,即自己和自己交换,成本为0
- 长度为2的圆,两个对换即可,成本为 w i + w j w_i+w_j wi+wj
- 长度大于2的圆,我们可以选取这个长度中最小的元素作为踏板,通过最小的的元素来移动其他元素可以保证成本最小。如图
4
,
7
,
1
,
5
4,7,1,5
4,7,1,5
此时总代价为:
其中 A [ i ] A[i] A[i]是除去不需要移动的元素以外剩余的元素。这里假设长度大于2的圆有n个元素,每个元素至少移动一次,所以 ∑ A [ i ] \sum A[i] ∑A[i]。最小值移动了 n − 2 n-2 n−2次(可以动手演算一下)。
这只是其中一种情况,可能我们将圆内最小值做踏板的代价不如将圆外最小值做踏板的代价小:
所以我们可以先选取整体的最小值 m i n ( w i ) min(w_i) min(wi)。
此时的总代价为:
我们来看一下代码的细节。
int ans = 0;
boolean[] V = new boolean[n];
for(int i=0;i<n;i++)
{
B[i] = A[i];
V[i] = false;
}
Arrays.sort(B,0,n);
for(int i=0;i<n;i++)
{
T[B[i]] = i;
}
首先初始化答案ans,设置一个V数组记录元素是否加入圆环。将A数组的值赋给B数组,我们需要一个排序好的数组来确定元素排序好后的位置。所以 T [ B [ i ] ] T[B[i]] T[B[i]]记录了B[i]的位置。
for(int i=0;i<n;i++)
{
if(V[i])
continue;
int cur = i;
int sum = 0;
int m = VMAX;
int an = 0;
while(true)
{
V[cur] = true;
an++;
int v = A[cur];
m = Math.min(v,m);
sum += v;
cur = T[v];
if(V[cur])
break;
}
ans += Math.min(sum + (an-2)*m,m+sum+(an+1)*s);
}
这段代码是整个程序的核心。for从0到n-1是保证每个数都参与到圆中。if判断则如果当前元素已经在圆中就判断下一个元素。如果不在,就记录当前元素在A中的位置,然后while循环中开始寻找i的位置应该是什么元素,依此类推,直到形成了闭环,也就是V[cur] = true,即当前判断元素已经加入了圆就终止while循环。最终比较两种方案的最小值加入ans即可。
完整代码
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
public class MinimumCostSort {
public static int MAX = 1000;
public static int VMAX = 10000;
static int[] B;
static int[] A;
static int[] T;
public static int n;
public static int s = VMAX;
public static void main(String[] args) {
B = new int[MAX];
A = new int[MAX];
T = new int[VMAX+1];
Scanner in = new Scanner(System.in);
n = in.nextInt();
for(int i=0;i<n;i++)
{
A[i] = in.nextInt();
s = Math.min(s,A[i]);
}
int ans = solve();
System.out.println(ans);
}
public static int solve()
{
int ans = 0;
boolean[] V = new boolean[n];
for(int i=0;i<n;i++)
{
B[i] = A[i];
V[i] = false;
}
Arrays.sort(B,0,n);
for(int i=0;i<n;i++)
{
T[B[i]] = i;
}
for(int i=0;i<n;i++)
{
if(V[i])
continue;
int cur = i;
int sum = 0;
int m = VMAX;
int an = 0;
while(true)
{
V[cur] = true;
an++;
int v = A[cur];
m = Math.min(v,m);
sum += v;
cur = T[v];
if(V[cur])
break;
}
ans += Math.min(sum + (an-2)*m,m+sum+(an+1)*s);
}
return ans;
}
}
题目链接ALDS1_6_D