HDOJ/HDU 2838 Cow Sorting
题意:每次交换数组相邻的两个位置的值,耗费是两个值的和。求使数组排序的最小花费;
解法:树状数组;
我的做法是从后往前遍历数组,对a[i],求出比a[i]小下标却比i大的元素个数x,然后求出这x个值的和temp,sum(a[i]*x+temp)(1<=i<=n)即为所求。至于为什么要这样做,原因很简单,大家都知道最少的交换次数是求逆序数。但是总花费最小是不是也是这样求?答案是:是。
这是可以证明的,因为每次只能交换相邻的两个数,所以只要存在a[i]>a[j](i<j)则a[i],a[j]则至少会交换一次。所以当他们只交换一次的时候总花费是最小的。
还有一个需要注意的地方是x必须是__int64。100000个数求任意一个数比他小且下标比他大的元素个数int型都不会溢出。但是如果这个值再乘以a[i]就可能会溢出。
贴代码:
#include <iostream>
#include <string.h>
#include <cstdio>
#include <algorithm>
using namespace std;
#define LL __int64
const int N = 100005;
int tree[N];
LL sum[N];
inline int Lowbit(int x) { return x & (-x); }
void Update(int x, int c) {
int i, maxn = N;
for (i = x; i < maxn; i += Lowbit(i)) {
tree[i] += c;
sum[i] += x;
}
}
LL Getsum(int x, LL &tot) {
int i;
LL temp = 0;
tot = 0;
for (i = x; i >= 1; i -= Lowbit(i)) {
temp += tree[i];
tot += sum[i];
}
return temp;
}
int a[N];
int main() {
int n;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(tree, 0, sizeof(tree));
memset(sum, 0, sizeof(sum));
LL s = 0;
for (int i = n; i >= 1; i--) {
LL temp;
LL x = Getsum(a[i], temp);
//如果x不是__int64那么x*a[i]可能会溢出,因为两个都不是__int64
s += (a[i] * x + temp);
Update(a[i], 1);
}
printf("%I64d\n", s);
}
}
浙公网安备 33010602011771号