最小成本排序 | Minimum Cost Sort | C/C++实现

问题描述

有重量为wi(i = 0, 1, …, n - 1)的n个货物排成一列。现在要用机械臂将这些货物排序。机械臂每次操作可以提起货物i和货物j并交换二者位置,同时产生wi + wj的成本。机械臂的操作次数没有限制。

请求出将给定货物列按重量升序排列时所需总成本的最小值。

输入: 第1行输入整数n。第2行输入n个整数wi(i = 0, 1, 2, …, n - 1),用空格隔开。
输出: 在1行内输出最小值。
限制:
1 ≤ n ≤ 1000
0 ≤ wi ≤ 10000
wi的值互不重复

输入示例

5
1 5 3 4 2

输出示例

7

讲解

我们以W = {4, 3, 2, 7, 1, 6, 5}为例进行分析。现在的目标是求出将W重排为{1, 2, 3, 4, 5, 6, 7}时所需的最小成本。我们不妨先画出一张标出每个元素最终将移动到哪个位置的草图。(如图1所示)
最小成本排序图1
图中有3个闭合的圆,分别是4 - 7 - 5 - 1 - 4;3 - 2 - 3;6 - 6。现在我们来分析每个圆所需的最小成本。

为自环即1长度的圆无需移动,成本为0。

长度为2交换位置即可,成本为二者之和。如3 - 2 - 3的成本为3 + 2 = 5。

对于长度大于等于3的圆。(如图2所示)在处理4 - 7 - 5 - 1 - 4时,通过1来移动其他元素可以保证成本最小。
最小成本排序图2
设圆中的元素为wi,圆内元素数为n,那么此时的成本为

Σ w i + ( n − 2 ) ∗ m i n ( w i ) \Sigma wi+ (n-2)*min(wi) Σwi+(n2)min(wi)

由于各元素至少移动一次,所以有 Σ w i \Sigma wi Σwi。再加上最小值在最后一次交换前要移动(n - 2)次,所以有(n - 2) * min(wi)。上式在n = 2时也成立。

于是最小成本为(5 + 0) + (17 + 2) = 24。

这个方法乍眼一看像那么回事,但是你仔细一寻思发现还存在反例。(如图3所示)
最小成本排序图3
如果套用之前的方法,1 - 2 - 1成本为3,8 - 10 - 9 - 7 - 8成本为48,总成本为51。

但若先将7和1交换,把圆8 - 10 - 9 - 7 - 8改为8 - 10 - 9 - 1 - 8,这部分的成本就成了28 + 2 * 1 = 30。然后再加上两次交换7和1以及圆1 - 2 - 1的成本,总成本为49。可见,即便我们多加了两次1和7交换的成本,总成本依然小于之前的方法,显然有时从圆外借元素来移动,能让成本更低。

设外圆的元素为x。借元素增加的成本为 2 ∗ ( m i n ( w i ) + x ) 2*(min(wi)+x) 2(min(wi)+x),节约的成本为 ( n − 1 ) ∗ ( m i n ( w i ) − x ) (n-1)*(min(wi)-x) (n1)(min(wi)x)此时该部分的总成本为

Σ w i + ( n − 2 ) ∗ m i n ( w i ) + 2 ∗ ( m i n ( w i ) + x ) − ( n − 1 ) ∗ ( m i n ( w i ) − x ) = Σ w i + m i n ( w i ) + ( n + 1 ) ∗ x \Sigma wi+(n-2)*min(wi)+2*(min(wi)+x)-(n-1)*(min(wi)-x)=\Sigma wi+min(wi)+(n+1)*x Σwi+(n2)min(wi)+2(min(wi)+x)(n1)(min(wi)x)=Σwi+min(wi)+(n+1)x

可见x应选用整个输入中最小的元素。

综上,程序需要计算“借整体最小元素”与“不借元素”的两种情况,选出其中成本较小的一方。

AC代码如下

#include<iostream>
#include<algorithm>
using namespace std;
static const int MAX = 1000;
static const int VMAX = 10000;

int n, A[MAX], s;
int B[MAX], T[VMAX+1];

int solve(){
	int ans = 0;
	
	bool V[MAX];
	for(int i = 0; i < n; i++){
		B[i] = A[i];
		V[i] = false;
	}
	sort(B, B+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 S = 0;
		int m = VMAX;
		int an = 0;
		while(1){
			V[cur] = true;
			an++;
			int v = A[cur];
			m = min(m, v);
			S += v;
			cur = T[v];
			if(V[cur]) break;
		}
		ans += min(S + (an - 2) * m, m + S + (an + 1) * s);
	}
	
	return ans;
} 

int main(){
	cin>>n;
	s = VMAX;
	for(int i = 0; i < n; i++){
		cin>>A[i];
		s = min(s, A[i]);
	}
	int ans = solve();
	cout<<ans<<endl;
	
	return 0;
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要在Android Studio中编写C/C++代码,你需要进行以下步骤: 1. 创建一个新的Android Studio项目。 2. 在你的项目中创建一个新的C/C++源文件。你可以使用菜单File->New->C/C++ Source File来创建源文件。 3. 在你的源文件中编写C/C++代码。 4. 在你的项目中创建一个新的JNI头文件。你可以使用菜单File->New->JNI/C++ Header File来创建头文件。 5. 在你的JNI头文件中声明你的C/C++函数和变量。例如,如果你的C/C++源文件中有一个函数叫做myFunction,你可以在你的JNI头文件中这样声明它: ```c++ JNIEXPORT void JNICALL Java_com_example_myapp_MyClass_myFunction(JNIEnv *env, jobject obj); ``` 请注意,这里的Java_com_example_myapp_MyClass_myFunction是根据你的Java类和函数名称来自动生成的。你需要根据你的实际项目进行修改。 6. 在你的Java代码中,使用JNI接口调用你的C/C++函数。例如,如果你的Java类是MyClass,你可以在它的某个方法中这样调用你的C/C++函数: ```java public void myMethod() { myFunction(); } ``` 7. 在你的项目中配置NDK环境。你需要下载NDK并在你的项目中配置NDK路径。你可以使用菜单File->Project Structure->SDK Location来配置NDK路径。 8. 在你的项目中配置CMake。你需要创建一个CMakeLists.txt文件来告诉Android Studio如何编译你的C/C++代码。你可以使用以下模板来创建CMakeLists.txt文件: ``` cmake_minimum_required(VERSION 3.10.2) project(myproject C CXX) add_library(mylib SHARED mysource.cpp) target_link_libraries(mylib log) ``` 请注意,这里的mysource.cpp和mylib是根据你的实际项目进行修改。 9. 运行你的项目并测试你的C/C++代码。 请注意,以上步骤只是一个基本的示例,你需要根据你的实际项目需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值