您的位置:首页 > 其它

poj 2838 Cow Sorting 树状数组 两种思路

2017-01-29 10:59 381 查看
题目:


Cow Sorting

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 3333    Accepted Submission(s): 1124


Problem Description

Sherlock's N (1 ≤ N ≤ 100,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" level in the range 1...100,000. Since grumpy cows are more likely to damage Sherlock's milking equipment, Sherlock would like to reorder the cows
in line so they are lined up in increasing order of grumpiness. During this process, the places of any two cows (necessarily adjacent) can be interchanged. Since grumpy cows are harder to move, it takes Sherlock a total of X + Y units of time to exchange two
cows whose grumpiness levels are X and Y.

Please help Sherlock calculate the minimal time required to reorder the cows.

 

Input

Line 1: A single integer: N

Lines 2..N + 1: Each line contains a single integer: line i + 1 describes the grumpiness of cow i.

 

Output

Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.

 

Sample Input

3
2
3
1

 

Sample Output

7

Hint
Input Details

Three cows are standing in line with respective grumpiness levels 2, 3, and 1.
Output Details

2 3 1 : Initial order.
2 1 3 : After interchanging cows with grumpiness 3 and 1 (time=1+3=4).
1 2 3 : After interchanging cows with grumpiness 1 and 2 (time=2+1=3).

给定n个数,任意交换相邻两个数得不降序列,每次交换代价为两数数值之和,求最小代价。

分析:

按照冒泡排序的方式交换可使得代价最小。每次把最小的冒到最前面去。

思路①对于第i个数a[i],如果它前面有sum_num(i)个数比它大,这些数的和为sum_val(i),则将a[i]交换到最终的位置需要a[i]*sum_num(i)+sum_val(i)

思路②对于第i个数a[i],如果它前面有a个数比它大,后面有b个数比它小,则整个过程中i共参与a+b次交换,i需要的交换次数为 a[i]*(a+b)

代码:

思路①:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll __int64
const int maxn=100009;

ll n,a[maxn],c[maxn],d[maxn],ans;//c用来维护数量,d用来维护和

ll get_sum(ll i){//求和
ll s=0;
while(i>0){
s+=d[i];
i-=i&(-i);
}
return s;
}

ll get_num(ll i){//求数量
ll s=0;
while(i>0){
s+=c[i];
i-=i&(-i);
}
return s;
}

void add(ll i,ll x){
while(i<=n){
c[i]+=1;
d[i]+=x;
i+=i&(-i);
}
}

int main(){//46MS	3912K
while(~scanf("%I64d",&n)){
ans=0;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
for(int i=1;i<=n;++i){
scanf("%d",a+i);
add(a[i],a[i]);
ans+=a[i]*(i-get_num(a[i]));//前面比a[i]大的数的个数
ans+=get_sum(n)-get_sum(a[i]);//比它大的数的和
}
printf("%I64d\n",ans);
}
return 0;
}


思路②:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll long long
const int maxn=100009;

ll n,a[maxn],c[maxn],ans;//c用来维护树状数组

ll sum(ll i){//求前缀和
ll s=0;
while(i>0){
s+=c[i];
i-=i&(-i);
}
return s;
}

void add(ll i,ll x){//结点值更新
while(i<=n){
c[i]+=x;
i+=i&(-i);
}
}

int main(){//62MS	3128K       注意全程 long long
while(~scanf("%I64d",&n)){
ans=0;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
for(int i=1;i<=n;++i){//正序建立树状数组,得到前面有多少个比它大
scanf("%d",a+i);
add(a[i],1);
ans+=a[i]*(i-sum(a[i]));
}
memset(c,0,sizeof(c));
for(int i=n;i>=1;--i){//逆序建立树状数组,得到后面有多少个比它小
add(a[i],1);
ans+=a[i]*sum(a[i]-1);
}
printf("%I64d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: