【openjudge】【前缀和】P6731啤酒厂选址

【描述】

海上有一个岛,在环海边上建有一条环岛高速公路,沿着公路有n(5 < n < 10000)个居民点,假设每个居民点有一个编号,从0开始,按顺时针依次从小到大(即,0,1,…,n-1)编号。在岛上啤酒很受青睐。某啤酒企业计划在岛上投资建一个啤酒厂,并根据啤酒需求每天向居住点送啤酒。已知两个相邻的居民点的距离以及每个居住点每天的啤酒需求量(假设每个居住点每天不超过2000桶)。假定每单位长度的路程送一桶啤酒需要的费用恒定(为单位费用)。请问,选择哪一个居民点建啤酒厂,才能使每天送啤酒的费用最小(空车不计费用)。

【输入】

输入第一行:为居民点数目n

后面为n行,每行为一个居民点的啤酒需求量以及按顺时针离下一个居民点的距离(均为整数,空格间隔),从编号为0的开始,按单增顺次给出。

注意:后面第n行对应于居民点(n-1)的啤酒需求量以及到编号为0的居民点距离。输出啤酒厂所在的居民点编号以及每天的运输费用,其间以逗号间隔

【输出】

输出啤酒厂所在的居民点编号以及每天的运输费用,其间以逗号间隔

输入:
6
500 10
300 30
350 25
400 60
700 28
200 35

输出:
0,94100
输入输出样例

【分析:】

暴力枚举n个点中的每一个点作为起点,计算每个点为起点时的最小花费,打擂找最小值即可

时间复杂度O(n2)

n最大是10000,按计算机每秒能进行一亿次操作(现在的计算机速度远远大于这个值)来算,刚刚好能够卡过

 

关于如何求出送啤酒的费用,可以利用前缀和来简化算法

求出第i个点到第一个点的距离f(i)(前缀和,1 <= i <= n)

点l、点r的距离就是f(r) - f(l - 1)

len[j]存的是j到j+1的长度,所以乘以第j个居民点需要的啤酒数量时,要从(j-1)点开始算

由于是环岛公路,从点i到点j的走法有两种:

  1. abs(len[j] - len[i - 1])
  2. sum - abs(len[j] - len[i - 1]) (sum为环状公路的总长度)

要从两种走法中选取最小的走法.

【代码:】

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 const int MAXN = 10000 + 1;
 6 int n, sum, pos, minn = 0x7fffffff;
 7 int a[MAXN], len[MAXN];
 8 
 9 inline int abs(int x) {
10     return x < 0 ? -x : x;
11 }
12 
13 int main() {
14     scanf("%d", &n);
15     for(int i = 1; i <= n; i++) {
16         scanf("%d%d", &a[i], &len[i]);
17         sum += len[i];
18         len[i] += len[i - 1];
19     }
20     for(int i = 1; i <= n; i++) {
21         int tmp = 0;
22         for(int j = 1; j <= n; j++)
23             //len[j]存的是j到j+1的长度 
24             //所以要从j-1开始算
25             //i、j大小关系不定,所以要加绝对值
26             tmp+=min(abs(len[j-1]-len[i-1]),sum-abs(len[j-1]-len[i-1]))*a[j];
27         if(minn > tmp) {
28             minn = tmp;
29             pos = i - 1;
30         }
31     }
32     printf("%d,%d", pos, minn);
33 }

from:

http://noi.openjudge.cn/ch0113/19/

原文地址:https://www.cnblogs.com/devilk-sjj/p/8998213.html