poj3270 Cow Sorting
2015-09-20 19:37
225 查看
给定有序数组a[1...n]的一个置换a[σ(1)...σ(n)], 通过交换数组元素把置换后的数组恢复为有序,
定义进行一次交换的代价为两元素之和,试问此过程的最小总代价。
实际上一种置换即定义S = {1,...,n}到其自身的一个双射函数f。
可以证明必然存在整数k使得f^k = f0 = I。即存在周期性。
实际上此函数的幂存在局部周期性,即存在若干个互不相交循环单位,S中每个元素恰属于其中一个循环节。
例如考虑:f:u(1,2,3,4,5,6,7) -> v(3,6,7,5,1,2,4)
将循环单位极小化:(3,7,5,1,4)(6,2)。
循环节内的元素按其在v中的位置排列。
我们的目标是将其调整为(1,3,4,5,7)(2,6)。
循环节内的每一个元素都在不合适的位置上,因此长度为l循环节内部至少需要进行(l - 1)次互换可使其有序。
考虑长度为l的循环节:
由于所有元素均需要调整,因此循环节内部调整代价的一个下界是循环节内所有元素之和加上最小元素与交换次数的乘积。
可以达到该下界:
例考虑调整(3,7,5,1,4),由于1占据了5应该所处的位置,(5,1)交换得到(3,7,1,5,4)。
如此不断迭代将其整理为有序所需代价为min_value * (l - 2) + sum_value(*)。
由于循环节内部调整相互独立,即可以分步进行,考虑利用非循环节内部元素进一步降低代价。
交换循环节外部一元素a(取最小的元素显然更优)和循环节内部一元素b,如此总代价为:
2 * (a + b) + a * (l - 2) + sum_value - b + a = b + a * (l + 1) + sum_value(#),
由于a取外部最小值,b取内部最小值,(#)可能会优于(*)。
(#)-(*) = a * (l + 1) - (l - 3) * b = (l + 1) * (a - b) + 4 * b,
由于已经引入全局最小值,若再次交换则必有a > b,只会增加代价。
因此最多交换一次。
这样本题就可以给出答案了。
http://poj.org/problem?id=3270
View Code
定义进行一次交换的代价为两元素之和,试问此过程的最小总代价。
实际上一种置换即定义S = {1,...,n}到其自身的一个双射函数f。
可以证明必然存在整数k使得f^k = f0 = I。即存在周期性。
实际上此函数的幂存在局部周期性,即存在若干个互不相交循环单位,S中每个元素恰属于其中一个循环节。
例如考虑:f:u(1,2,3,4,5,6,7) -> v(3,6,7,5,1,2,4)
将循环单位极小化:(3,7,5,1,4)(6,2)。
循环节内的元素按其在v中的位置排列。
我们的目标是将其调整为(1,3,4,5,7)(2,6)。
循环节内的每一个元素都在不合适的位置上,因此长度为l循环节内部至少需要进行(l - 1)次互换可使其有序。
考虑长度为l的循环节:
由于所有元素均需要调整,因此循环节内部调整代价的一个下界是循环节内所有元素之和加上最小元素与交换次数的乘积。
可以达到该下界:
例考虑调整(3,7,5,1,4),由于1占据了5应该所处的位置,(5,1)交换得到(3,7,1,5,4)。
如此不断迭代将其整理为有序所需代价为min_value * (l - 2) + sum_value(*)。
由于循环节内部调整相互独立,即可以分步进行,考虑利用非循环节内部元素进一步降低代价。
交换循环节外部一元素a(取最小的元素显然更优)和循环节内部一元素b,如此总代价为:
2 * (a + b) + a * (l - 2) + sum_value - b + a = b + a * (l + 1) + sum_value(#),
由于a取外部最小值,b取内部最小值,(#)可能会优于(*)。
(#)-(*) = a * (l + 1) - (l - 3) * b = (l + 1) * (a - b) + 4 * b,
由于已经引入全局最小值,若再次交换则必有a > b,只会增加代价。
因此最多交换一次。
这样本题就可以给出答案了。
http://poj.org/problem?id=3270
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1e4 + 10; const int inf = 0x3f3f3f3f; int f[maxn]; int a[maxn], n, mini; bool vis[maxn]; void solve(){ memset(vis, 0, sizeof vis); int ans = 0; for(int i = 0; i < n; i++){ if(vis[i]) continue; vis[i] = 1; int len = 1, minii = a[i], sum = a[i]; int j = f[i]; while(j != i){ vis[j] = 1; sum += a[j]; ++len; minii = min(minii, a[j]); j = f[j]; } ans += (len - 2) * minii + sum + min(0, (len + 1) * (mini - minii) + 4 * minii); } printf("%d\n", ans); } bool cmp1(int u, int v) { return a[u] < a[v]; } int main(){ freopen("in.txt", "r", stdin); while(~scanf("%d", &n)){ mini = inf; for(int i = 0; i < n; i++){ scanf("%d", &a[i]); mini = min(a[i], mini); } for(int i = 0; i < n; i++) f[i] = i; sort(f, f + n, cmp1); solve(); } return 0; }
View Code
相关文章推荐
- 软工实践练习一——个人
- openssl for android使用
- 【bzoj2709】 迷宫花园 spfa
- 深入理解Java的接口和抽象类
- 我的感想
- Codeblocks中文字体反转
- 找出矩阵中的最大矩形
- 我的架构经验小结(四)—— 实战中演化的三层架构
- git
- 502 Bad Gateway 自动重启脚本
- 如何成为一名优秀的软件测试工程师
- cout 小数点后位数(转)
- 迎接新未来
- # 弱鸡 Linux Mint 17.2安装体验
- 等价类方法
- OC字符串实现路径合并
- 使用Git进行代码管理心得------------个人练习
- 负载均衡
- netbeans做简易的计算器
- [杂记]一些感悟,随时更新