Problem
portal:Minimum Cost Sort
Description
You are given n integers wi(i=0,1,…,n−1) to be sorted in ascending order. You can swap two integers wi and wj. Each swap operation has a cost, which is the sum of the two integers wi+wj. You can perform the operations any number of times.
Write a program which reports the minimal total cost to sort the given integers.
Input
In the first line, an integer n is given. In the second line, n integers wi(i=0,1,2,…n−1) separated by space characters are given.
Output
Print the minimal cost in a line.
Constrains
1≤n≤1,000
0≤wi≤104
wi are all different
Sample
Sample Input1
5
1 5 3 4 2
Sample Output1
7
Sample Input2
4
4 3 2 1
Sample Output2
10
Solution
First try
Analysis
只看到了两个最普通的示例, 然后就简单的认为只需要把最后面的最小的元素移动到有序时他应该存在的位置就行了
Design
一个数组用于存储所有的输入, i 指向首元素, minSub 指向后面的元素中最小的, 然后与i进行交换, i向后移动一位, 一直循环, 直到结束
Code
找不到了emmm
Result
只有前两个示例通过了, 从第三个开始就错误了.
Second try
Analysis
第三个样例如下:
in
4
10 7 8 9
out
48
通过样例分析, 按照我第一次尝试的方法, 将会移动很多次10, 从而不能得到最小的消耗结果, 最后通过两个数组的比较, 7是最小的一个, 它这个位置本来应该是8, 于是交换7和8, 然后7到了第三个位置, 这个位置本来是9, 继续交换, 最后一次与10进行交换, 得到最小消耗.
于是:
- 读取输入存储到数组中, 然后创建一个新的数组, 拷贝所有数据, 将新数组排序
- 找到原数组中最小的一个值的下标
- 与排好序的数组进行比较, 如果排好序的数组中的该下标对应的值与当前值相同, 则跳到步骤6, 否则进入步骤4
- 在乱序数组中查找排序数组中该下标对应的值在乱序数组中的下标
- 进行交换, 消耗值增加, 将当前下标修改为交换后的下标, 进入步骤3
- 用于查找最小值的范围缩小, 因为处理之后的最小值去了左边, 所以查找范围的左边界向右移动, 继续在剩余的元素中查找, 如果只剩余一个元素, 则进入步骤7 , 否则进入步骤3
- 输出结果
Design
两个数组, 一个存储原始数据, 一个使用快速排序排好序, 用于到时候比较.
一个函数, 功能为通过值找到它在数组中的下标
一个函数, 功能为查找数组在一定范围内的最小值所对应的下标
然后就是一些循环用的变量, 存储结果的变量
Code
#include <iostream>
#include <algorithm>
using namespace std;
int findMinSub(int a[], int left, int right);
int findNumSub(int a[], int num, int left, int right);
int main(void)
{
int n, i, total = 0;
cin >> n;
int *w = new int[n];
int *s = new int[n];
for (i = 0; i < n; i++)
{
cin >> w[i];
s[i] = w[i];
}
sort(s, s+n);
int left = 0, right = n, minSub, sub;
while (left + 1 < right)
{
minSub = findMinSub(w, left, right);
while (w[minSub] != s[minSub])
{
sub = findNumSub(w, s[minSub], left, right);
total += w[minSub] + w[sub];
swap(w[minSub], w[sub]);
minSub = sub;
}
left += 1;
}
cout << total << endl;
}
int findMinSub(int a[], int left, int right)
{
int minSub = left;
int i;
for (i = left; i < right; i++)
if (a[i] < a[minSub])
minSub = i;
return minSub;
}
int findNumSub(int a[], int num, int left, int right)
{
int i;
for (i = left; i < right; i++)
if (a[i] == num)
return i;
return -1;
}
Result
正确了14个, 从第15个开始然后错了, 由于错的样例有21个数据, 我的能力不足以人工处理找到最好的解题方法, 只好观看解析
Third try
Analysis
分析部分见书《挑战程序设计竞赛:算法和数据结构》
看完解析之后没有直接看代码, 而是自己尝试实现
Design
对于每一个圆, 都有自己的元素, 没有那个元素同时在两个圆里, 所以创建一个数组, 用来确定该元素是非分派到了圆中(代码中为c数组)
最后对于每一个圆, 都有一个通用的计算公式, 所以我创建了一个结构体, 并没有存储这些元素, 而是用来存储圆的相关信息, 元素数量, 元素和, 最小元素, 方便最后使用公式计算, 结构体中有一个add方法, 更新元素和, 元素数量, 并判断是否为最小值, 修改最小元素.
Code
#include <iostream>
#include <algorithm>
using namespace std;
struct Ci {
int n;
int sum;
int min;
Ci() {
n = 0;
sum = 0;
min = 10000;
}
void add(int x) {
if (min > x)
{
min = x;
}
sum += x;
n++;
}
} ci[1000];
int findNumSub(int a[], int len, int num)
{
int i = 0;
for (i = 0; i < len; i++)
{
if (a[i] == num)
return i;
}
return -1;
}
int main(void)
{
int n, minValue, i;
cin >> n;
int *a = new int[n], *b = new int[n], *c = new int[n];
for (i = 0; i < n; i++)
{
cin >> a[i];
b[i] = a[i];
c[i] = 0;
}
sort(b, b + n);
minValue = b[0];
int no = 0, num, sub;
for (i = 0; i < n; i++)
{
if (c[i] != 0)
continue;
sub = i;
while (c[sub] == 0)
{
num = a[sub];
ci[no].add(num);
c[sub] = 1;
sub = findNumSub(b, n, num);
}
no++;
}
int total = 0;
int m1, m2;
for (i = 0; i < no; i++)
{
if (ci[i].n == 1)
continue;
m1 = ci[i].sum + (ci[i].n - 2) * ci[i].min;
m2 = ci[i].sum + ci[i].min + (ci[i].n + 1) * minValue;
if (m1 < m2)
total += m1;
else
total += m2;
}
cout << total << endl;
}
Result
通过了所有测试用例, 学到了结构体在c++中和类几乎差不多, 不过默认所有元素都是public的, 而不是私有的.