1633 Moo Volume解题报告

 

Moo Volume

时间限制(普通/Java):1000MS/10000MS     运行内存限制:65536KByte
总提交: 33            测试通过: 11
http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1633

描述

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.

输入

* Line 1: N

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

输出

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

 

样例输入

 

5
1
5
3
2
4

 

样例输出

 

40

 

提示

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.

 

 

 

 

分析:

如果最笨的方法是按照说的模拟,这样时间复杂度n^2  就是10000^2应该超时了,那么可以再进行优化吗?答案当然是可以的。

首先想第一个要减去后面的数,后面的数也要减去前面的数,这样不就都重复减了一次吗,所以说互相之间只要减去一次的和乘以2就是答案了。

一半怎么求呢?

开个sum变量,初值赋0,

第一个去减去后面的所有数的和加到sum里,然后第二个减去后面所有的数和加到sum里,依次直到最后一个。

这样sum里就是一半了,答案:sum*2.

好了,来看看时间复杂度:n-1,n-2,n-3....0=n*(n-1)/2

那么就是10000*(10000-1)/2大概5000万左右.如果数据强点那么肯定超时了。

好现在来看看更好的方法,网上有个公式一样的,不过我没看明白,

大家看看:

题意:输入n个数{vol[i]}(n<=10000,vol[i]<=10^9) 试求出 ∑|vol[i]-vol[j]|。

思路:(1)先对这n个数进行从小到大排序,vol[1] <= vol[2] <= vol[3] …… vol[n - 1] <= vol[n];

       (2) sum += (vol[i + 1] - vol[i]) * (i + 1) * (n - i - 1) (1 <= i < n) 

      (3)ans = 2 * sum; 

源代码:

 

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 10000;
int main()
{
      __int64 vol[MAXN];
      __int64 i, n, sum = 0;
      scanf("%d", &n);
      for(i = 0; i < n; i ++) scanf("%I64d", &vol[i]);
      sort(vol, vol + n);
      for(i = 0; i < n - 1; i ++)
            sum += (vol[i + 1] - vol[i]) * (i + 1) * (n - i - 1);
      sum *= 2;
      printf("%I64d/n", sum);
      return(0);
}

 

 

 

没看明白??没关系,我们来看看另外一种方法,DP(暂时叫DP,感觉不太像,但当时思路是DP下手的)也可以解决。

时间复杂度n
来说说思路:(开变量dp加上前面搜过的数字)从头开始循环,我们这里从后面数往前面减。搜索从第一个开始,前面没数字,不用减,

到第二个,刚才已经搜索过第一个了,那么知道后面的数字要减去前面的,首先把第一个加到dp中,然后只要(第二个数字*i-dp),

然后加到sum里去。

i表示当前数字前面有多少数。好理解吧。看代码吧实在不知道。

对了,先对他递增排序~

 

举个例子吧,更加好理解点:

3

a1

a2

a3

我用一般形式表示。

那么我们要求的sum是=|a1-a2|+[a1-a3|+|a2-a1|+|a2-a3|+|a3-a1|+|a3-a2|

整理下就是sum=2*|a2-a1|+2*|a3-a1|+2*|a3-a2|=2*(|a2-a1|+|a3-a1|+|a3-a2|)

如果排序了从小到大,那么sum=2*(a2-a1+a3-a1+a3-a2)=2*(a2-a1+a3-(a2+a1))

 推广到

n

a1

a2

.

.

an

那么就是sum=2*(a2-a1+a3-(a2+a1)+a4-(a3+a2+a1)+.....an-(an-1+an-2+...a1))

前面的dp就是保存an-1+an-2+...a1的。                                                                       

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helihui123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值