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

来源:互联网 发布:苹果电脑mac地址 编辑:程序博客网 时间:2024/05/01 15:47

题目:

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
3231
 

Sample Output
7
Hint
Input DetailsThree cows are standing in line with respective grumpiness levels 2, 3, and 1.Output Details2 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 __int64const 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(){//46MS3912Kwhile(~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 longconst 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(){//62MS3128K       注意全程 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;}



0 0
原创粉丝点击