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

本文介绍了一道关于重新排列一群奶牛以降低损坏风险的问题CowSorting,并提供了两种高效的算法实现思路,通过使用树状数组来优化计算过程,实现了求解最小交换代价的目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值