POJ 2231 解题报告 排序+递推+高精度运算

题目:

Moo Volume
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 4515 Accepted: 1190

Description

Farmer John has received a noise complaint from his neighbor, Farmer Bob, stating that his cows are making too much noise. FJ's N cows (1 <= N <= 10,000) all graze at various locations on a long one-dimensional pasture. The cows are very chatty animals. Every pair of cows simultaneously carries on a conversation (so every cow is simultaneously MOOing at all of the N-1 other cows). When cow i MOOs at cow j, the volume of this MOO must be equal to the distance between i and j, in order for j to be able to hear the MOO at all. Please help FJ compute the total volume of sound being generated by all N*(N-1) simultaneous MOOing sessions.

Input

* Line 1: N * Lines 2..N+1: The location of each cow (in the range 0..1,000,000,000).

Output

There are five cows at locations 1, 5, 3, 2, and 4.

Sample Input

5
1
5
3
2
4

Sample Output

40

Hint

INPUT DETAILS: There are five cows at locations 1, 5, 3, 2, and 4. OUTPUT DETAILS: Cow at 1 contributes 1+2+3+4=10, cow at 5 contributes 4+3+2+1=10, cow at 3 contributes 2+1+1+2=6, cow at 2 contributes 1+1+2+3=7, and cow at 4 contributes 3+2+1+1=7. The total volume is (10+10+6+7+7) = 40.

Source

这道题目意思是要求直线上N个点相互之间距离的总和。一般就是建立一个二重循环,复杂度为O(n^2),由于本题目n的最大值可以达到1000000000,因此这样做肯定超时。解答思路是先根据点的位置从小到大对点进行排序,并求出排好序后的点中第1个点到其它点的距离:s(0)。接着递推求出s(i), i=1,2,...N-1。假设已知s(i),通过s(i)与s(i+1)的递推关系,求出s(i+1)。d(i,i+1)表示第i个点与第i+1个点之间的距离。

s(i+1) = Sum(d(i+1,j), j != i+1)

           = Sum(d(k1,i+1),0<=k1<i+1) + Sum(d(i+1,k2),i+1<k2<N)

          = Sum(d(k1,i) + d(i,i+1),0<=k1<i+1) + Sum(d(i,k2) - d(i,i+1),i+1<k2<N)

          = Sum(d(k1,i),0<=k1<i+1) + (i+1) * d(i,i+1) + Sum(d(i,k2),i+1<k2<N) - (N-i-2) * d(i,i+1)

          = Sum(d(k1,i), 0<=k1<i) + Sum(d(i,k2),i+1<k2<N) + (i+1) * d(i,i+1) - (N-i-2) * d(i,i+1)

          = Sum(d(k1,i), 0<=k1<i) + Sum(d(i,k2),i+1<=k2<N) + i * d(i,i+1) - (N-i-2) * d(i,i+1)

          = s(i) + i * d(i,i+1) - (N-i-2) * d(i,i+1)

从这个递推式可以看出,s(i+1)只与s(i)相关,因此在实际实现中不需要开一个数组来记录每个s(i),而只需要记录上一次求出的s(i)。每次求出s(i),就累加到最后的总和中。

由于本题的数值特别大,总和的最大值可以达到10^17,每个s(i)可以达到10^13。第一次实现用double来表示这些数据,结果一直出错。后来用了一个高精度的模板进行了修改才通过。double不能通过的原因可能是跟有效数字位数有关,double的有效数字位数为15~16位,虽然double完全能够满足本题中的数值范围。高精度的程序来自网上一个模板。

源代码:

#include <iostream>

using namespace std;



const int Base=1000000000;

const int Capacity=100;



struct BigInt{

    int Len;

    int Data[Capacity];

    BigInt():Len(0){}

    BigInt(const BigInt &V):Len(V.Len){memcpy(Data,V.Data,Len*sizeof*Data);}

    BigInt(int V):Len(0){for(;V>0;V/=Base) Data[Len++]=V%Base;}

    BigInt(unsigned long V):Len(0){for(;V>0;V/=Base) Data[Len++]=V%Base;}

    BigInt &operator=(const BigInt &V){Len=V.Len;memcpy(Data,V.Data,Len*sizeof*Data);return *this;}

    int &operator[](int Index){return Data[Index];}

    int operator[](int Index)const{return Data[Index];}

};

int compare(const BigInt &A,const BigInt &B){

    if(A.Len!=B.Len) return A.Len>B.Len ? 1:-1;

    int i;

    for(i=A.Len-1;i>=0 && A[i]==B[i];i--);

    if(i<0)return 0;

    return A[i]>B[i] ? 1:-1;

}

BigInt operator+(const BigInt &A,const BigInt &B){

    int i,Carry(0);

    BigInt R;

    for(i=0;i<A.Len||i<B.Len||Carry>0;i++){

        if(i<A.Len) Carry+=A[i];

        if(i<B.Len) Carry+=B[i];;

        R[i]=Carry%Base;

        Carry/=Base;

    }

    R.Len=i;

    return R;

}

BigInt operator-(const BigInt &A,const BigInt &B){

    int i,Carry(0);

    BigInt R;

    R.Len=A.Len;

    for(i=0;i<R.Len;i++){

        R[i]=A[i]-Carry;

        if(i<B.Len) R[i]-=B[i];

        if(R[i]<0) Carry=1,R[i]+=Base;

        else Carry=0;

    }

    while(R.Len>0&&R[R.Len-1]==0) R.Len--;

    return R;

}

BigInt operator*(const BigInt &A,const int &B){

    int i;

    unsigned long int Carry = 0;

    BigInt R;

    for(i=0;i<A.Len||Carry>0;i++){

        if(i<A.Len) Carry+=A[i]*B;

        R[i]=Carry%Base;

        Carry/=Base;

    }

    R.Len=i;

    return R;

}

istream &operator>>(istream &In,BigInt &V){

    char Ch;

    for(V=0;In>>Ch;){

        V=V*10+(Ch-'0');

        if(In.peek()<=' ') break;

    }

    return In;

}

ostream &operator<<(ostream &Out,const BigInt &V){

    int i;

    Out<<(V.Len==0 ? 0:V[V.Len-1]);

    for(i=V.Len-2;i>=0;i--) for(int j=Base/10;j>0;j/=10) Out<<V[i]/j%10;

    return Out;

}



static int f(const void *a, const void *b)

{

	unsigned long r1 = (*((unsigned long *)a));

	unsigned long r2 = (*((unsigned long *)b));



	if(r1 == r2)

		return 0;

	if(r1 > r2)

		return 1;

	return -1;

}





int main(int argc,char **argv)

{



	unsigned long T[10000];



	int N;

	cin>>N;



	BigInt cur;

	BigInt sum;



	int i;



	for(i=0;i<N;i++)

		cin>>T[i];



	qsort(T,N,sizeof(T[0]),f);



	for(i=1;i<N;i++)

	{

		unsigned long val = T[i] - T[0];

		BigInt valInt(val);

		cur = (cur + valInt);

	}



	sum = (sum + cur);



	for(i=0;i<N-1;i++)

	{

		int A = i;

		int B = N-i-2;



		BigInt v(T[i+1] - T[i]);



		BigInt w1 = v*A;

		BigInt w2 = v*B;



		cur = (cur + w1);

		cur = (cur - w2);



		sum = (sum + cur);

	}



	cout<<sum<<endl;



	/*



	unsigned long T[10000];



	int N;



	double cur = 0;



	long double sum = 0;



	char buf[1000];



	int i,j;



	cin>>N;



	for(i=0;i<N;i++)

		cin>>T[i];



	qsort(T,N,sizeof(T[0]),f);



	for(i=1;i<N;i++)

	{

		cur = cur + T[i] - T[0];

	}



	sum = sum + cur;



	for(i=0;i<N-1;i++)

	{

		int A = i;

		int B = N - i - 2;

		

		double d = T[i+1] - T[i];



		cur = cur + A * d - B * d;



		sum = sum + cur;



	}

    

    sprintf(buf,"%f",sum);



	j = 0;



	while(buf[j] && buf[j] != '.')

		cout<<buf[j++];



	cout<<endl;



    */



	return 0;

}
main函数中就是第一次用double来表示写的程序

高精度程序来自于网络。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值