【题目链接】

OpenJudge NOI 1.13 19:啤酒厂选址

【题目考点】

1. 枚举

【解题思路】

一个有n个数字的环,顺时针标号从0到n-1,顺时针取下一个数字的方法为i = (i+1)%n,逆时针取下一个数字的方法为i = (i-1+n)%n

解法1:枚举

枚举,i从1循环到n,在第i个居民点建厂,求在这里建厂时从这里到所有其它居民点运送啤酒的费用加和。
从i的顺时针下一个居民点开始,环形遍历每个居民点j,记录从第i居民点顺时针到第j居民点的路程为s,环岛总路程为s_tot,那么s_tot-s就是逆时针走到第j居民点的路程。比较s与s_tot-s,二者较小值即为从i到j的最短路程距离,距离乘以这里的啤酒需求量,即为费用。
到每个居民点的费用加和,即为在第i居民点建酒厂的总费用。
求出在每个居民点建酒厂产生的费用,求最小值。
该方法复杂度 O ( n 2 ) O(n^2) O(n2)

解法2:双指针

以下叙述中,i顺时针下一个数字记为i+1,逆时针下一个数字记为i-1。
d[i]表示第i居民点到第i+1居民点的距离。
假设在居民点i建酒厂,记第j居民点是最后一个从i出发顺时针走更近的居民点,即从i出发到所有i+1~j的居民点,都是顺时针走更近。从i出发到所有j+1~i-1的居民点,都是逆时针走更近。
这里的双指针指的就是酒厂地址i与最后一个从i出发顺时针走更近的居民点j。每次让i取顺时针的下一个位置,根据j的定义调整j的位置。
同时调整顺时针能走到的最远距离sr,逆时针能走的最远距离sl,顺时针到达的居民点需要的啤酒总数量br,逆时针到的居民点需要的啤酒总数量bl,以及总费用pr。
首先求出在第0位置建酒厂时,j, sr, sl, br, bl, pr各项的数值。

k从1顺时针遍历,sr不断增大,直到如果顺时针到k的距离sr+d[k-1]大于逆时针到k的距离s_tot-sr-d[k-1],那么就应该逆时针到k,j应该取k-1。同时更新br,pr。
再从n-1开始逆时针遍历到j+1,sl不断增大。同时更新bl,pr。

建酒厂的位置从1遍历到n-1,当建厂位置变为顺时针的下一个位置后,

  • 如果酒厂在i-1位置时对所有的居民点都是逆时针走到的,那么满足 j = i − 1 j = i-1 j=i−1。酒厂移动到第i位置后,逆时针到达的居民点减少了第i居民点,增加了第i-1居民点。
  • 除了以上情况,酒厂移动到第i位置后,顺时针到达的居民点减少了第i居民点,逆时针到达的居民点增加了第i-1居民点。

根据不同情况调整sr, sl, br, bl, pr。

此时i已经增加了1,看i到哪些定居点的行进方向发生了变化,也就是看j的变化。
j变为顺时针的下一个,看当前第i居民点到第j居民点是顺时针走更近还是逆时针走更近。

  • 如果是顺时针走更近,那么总费用减少了从i-1逆时针走送啤酒到j的费用,增加了从i顺时针走送啤酒到j的费用。而后让j增加1,再看到下一个居民点是否需要变换行进方向。
  • 如果到第j居民点是逆时针走更近,那么让j回到上一个位置,始终让j表示最后一个从i出发顺时针走更近的居民点,结束循环。
  • 以上过程进行中,需要不断调整sr, sl, br, bl, pr的值。

求出在第i位置建酒厂的总费用pr后,与已知的最小总费用左比较,求出费用的最小值。
该方法复杂度 O ( n ) O(n) O(n)

【题解代码】

解法1:枚举 复杂度: O ( n 2 ) O(n^2) O(n2)

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10005
int main()
{int s_tot = 0, n, a[N], d[N], mn = INF, mi = 0;//tot:环岛总长,a[i]:第i居民点啤酒需求量,d[i]为i到i+1位置的举例,p[i]在第i位置建厂每天运啤酒的费用, c单位费用scanf("%d", &n);for(int i = 0; i < n; ++i){scanf("%d %d", &a[i], &d[i]);s_tot += d[i];}for(int i = 0; i < n; ++i)//遍历居住点{int f = 0, s = 0, sd;//f:总费用 j:当前看的居住点 s:累积距离 sd:从j到i的最短距离for(int j = (i+1)%n; j != i; j = (j+1) % n)//从i的下一个居住点开始,环形遍历每个居住点 {s += d[(j-1+n)%n];sd = s_tot - s < s ? s_tot - s : s;f += sd * a[j];}if(f < mn){mn = f;mi = i;}}printf("%d,%d", mi, mn);return 0;
}

解法2:双指针 复杂度: O ( n ) O(n) O(n)

#include<bits/stdc++.h>
using namespace std;
#define N 10005
int main()
{int s_tot = 0, n, b[N], d[N];//b[i]:第i居民点的啤酒需求量 d[i]:第i居民点到第i+1居民点的距离 scanf("%d", &n);for(int k = 0; k < n; ++k){scanf("%d %d", &b[k], &d[k]);s_tot += d[k];//s_tot:环总长}int i = 0, j = n-1;//i:在第i位置建厂 j:从酒厂顺时针走到达更近的最后一个居民点,从酒厂到(j+1)%n居民点就是逆时针走更近。如果下面循环结束也没有设j,那么所有顶点都是通过顺时针走到的,j应该为n-1 int bl = 0, br = 0;//br:顺时针走到的居民点的啤酒需求量 bl:逆时针走到的居民点的啤酒需求量 int sl = 0,  sr = 0;//sr:从i开始顺时针走过的距离  sl:从i开始逆时针走过的距离 int pr = 0;//总费用for(int k = 1; k < n; k++){if(sr + d[k-1] <= s_tot - sr - d[k-1])//如果从i顺时针走到k的距离小于等于逆时针走到k的距离 {sr += d[k-1];br += b[k];//顺时针走到的居民点的啤酒需求量增加 pr += sr * b[k];//费用增加 }else{j = k-1;//取前一个位置 为顺时针能走到的最后一个位置 break;}}for(int k = n-1; k != j; k--)//从i的前一个开始逆时针遍历直到j{sl += d[k];//逆时针走的距离 pr += sl * b[k];//此时sl为逆时针走到k的距离 bl += b[k];}int mnp = pr, mni = 0;//此时pr为在第0居民点建厂的总费用。mnp:费用最小值 mni:使得费用最小的啤酒厂位置 for(i = 1; i <= n-1; ++i)//酒厂从i-1移动到i{ if(j == (i-1)%n)//在i-1时,如果所有位置都要逆时针走到,j等于i逆时针的下一个。 {//酒厂移动到第i位置后,逆时针到达的居民点减少了第i居民点,增加了第i-1居民点。j = i;bl = bl - b[i] + b[i-1];pr += bl * d[i-1] - b[i] * sl;//bl的啤酒需求运送距离增加d[i-1]。向第i居民点原来需要逆时针走sl送啤酒,现在不用送了。 sl += d[i-1] - d[i];}else{//酒厂移动到第i位置后,顺时针到达的居民点减少了第i居民点,逆时针到达的居民点增加了第i-1居民点。sr -= d[i-1];sl += d[i-1];bl += b[i-1];pr += (bl - br) * d[i-1];//顺时针移动一个位置后,对于br的啤酒需求,距离变短,对于bl的啤酒需求,距离变长。 br -= b[i];}while(sr + d[j] < s_tot - sr - d[j])//如果到第j居民点顺时针走距离更短 {sr += d[j];j = (j + 1) % n;bl -= b[j];//到第j居民点从逆时针走到变为顺时针走到br += b[j];pr += (sr - sl) * b[j];//此时sl为i逆时针到j的距离,sr为i顺时针到j的距离 sl -= d[j];}if(pr < mnp){mnp = pr;mni = i;}}printf("%d,%d", mni, mnp); return 0;
}

【测试数据】

亲测解法2确实降低了复杂度,这里给一个生成数据的程序。由于数据太大会让结果超出int范围,这里人为缩小了数据的范围(每个居民点啤酒需求小于等于200,两个居民点间的距离小于等于10)。

#include <bits/stdc++.h>
using namespace std;
int main()
{srand(time(NULL));freopen("test.txt", "w", stdout);int n = 10000;cout << n << endl;for(int i = 1; i <= n; ++i){int a = rand()%200+1, b = rand()%10+1;cout << a << ' ' << b << endl; }fclose(stdout);return 0;
}

生成了一组n为10000的数据进行测试,解法1的代码需要运行1s以上,而解法2的代码总能在1s内完成。
本题正解应该是复杂度为 O ( n ) O(n) O(n)的解法2。由于OJ数据较水,因此复杂度为 O ( n 2 ) O(n^2) O(n2)的解法1也能过。

OpenJudge NOI 1.13 19:啤酒厂选址相关推荐

  1. OpenJudge NOI 1.13 07:玛雅历

    [题目链接] OpenJudge NOI 1.13 07:玛雅历 [题目考点] 1. 数组 2. 取模运算 3. stl map [解题思路] 输入Haab历的年月日,先确定该日期是从0年0月0日开始 ...

  2. 信息学奥赛一本通 1226:装箱问题 | OpenJudge NOI 4.6 19:装箱问题

    [题目链接] ybt 1226:装箱问题 OpenJudge NOI 4.6 19:装箱问题 [题目考点] 1. 贪心 [解题思路] 该题说是三维立方体,实际上无论是包裹还是产品,高度都是h,因而不用 ...

  3. 信息学奥赛一本通 1175:除以13 | OpenJudge NOI 1.13 27:除以13

    [题目链接] ybt 1175:除以13 OpenJudge NOI 1.13 27:除以13 [题目考点] 1. 高精度 考察:高精除低精 高精模低精 高精度计算讲解 [题解代码] 解法1:使用函数 ...

  4. 信息学奥赛一本通 1307:【例1.3】高精度乘法 | 1174:大整数乘法 | OpenJudge NOI 1.13 09:大整数乘法

    [题目链接] ybt 1307:[例1.3]高精度乘法 ybt 1174:大整数乘法 OpenJudge NOI 1.13 09:大整数乘法 [题目考点] 1. 高精度 考察:高精乘高精 高精度计算讲 ...

  5. 信息学奥赛一本通 1413:确定进制 | OpenJudge NOI 1.13 34:确定进制 | OpenJudge NOI 2.1 1973:确定进制

    [题目链接] ybt 1413:确定进制 OpenJudge NOI 1.13 34:确定进制 OpenJudge NOI 2.1 1973:确定进制 注意:两OJ平台上题目条件不同,ybt中:2≤b ...

  6. 信息学奥赛一本通 1412:二进制分类 | OpenJudge NOI 1.13 36:二进制分类

    [题目链接] ybt 1412:二进制分类 OpenJudge NOI 1.13 36:二进制分类 本题为:NOIP1995复赛 普及组 第三题 [题目考点] 1. 数制 2. 函数 [解题思路] 设 ...

  7. 信息学奥赛一本通 1411:区间内的真素数 | OpenJudge NOI 1.13 23:区间内的真素数

    [题目链接] ybt 1411:区间内的真素数 OpenJudge NOI 1.13 23:区间内的真素数 [题目考点] 1. 质数 2. 数字拆分 [解题思路] 设函数判断一个数是否是质数 设函数求 ...

  8. 信息学奥赛一本通 2050:【例5.20】字串包含 | OpenJudge NOI 1.17 19:字符串移位包含问题

    [题目链接] ybt 2050:[例5.20]字串包含 OpenJudge NOI 1.17 19:字符串移位包含问题 [题目考点] 1. 字符串 2. 判断一个字符串是不是另一个字符串的子串(字符串 ...

  9. 信息学奥赛一本通 1149:最长单词2 | OpenJudge NOI 1.13 16

    [题目链接] ybt 1149:最长单词2 OpenJudge NOI 1.13 16:最长单词2 [题目考点] 1. 字符串遍历 2. 处理多个字符串 3. while(cin >> - ...

最新文章

  1. PyTorch 笔记(03)— Tensor 数据类型分类(默认数据类型、CPU tensor、GPU tensor、CPU 和 GPU 之间的转换、数据类型之间转换)
  2. Google 希望将 Go 打造成云端应用开发的首选语言
  3. bzoj1046[HAOI2007]上升序列
  4. java父类shape_java父类为抽象类,子类构造方法传参
  5. leetcode 61. Rotate List
  6. PS使用技巧(五) 形状工具 U
  7. 前端JS/TS面试题
  8. 固态硬盘SSD闪存芯片的颗粒类型 和 颗粒等级
  9. python按什么键停止运行_python如何停止运行
  10. Sky Computing:利用空间异构分布式计算特性加速联邦学习
  11. 雨水情测报系统+智慧水库大坝安全监测系统
  12. Flink实战——每隔5秒,统计最近10秒的窗口数据
  13. 微信支付不能调起微信支付页面
  14. php 单引号 双引号的区别
  15. leetcode刷题之 树(14)-递归:找出二叉树中第二小的节点
  16. ES查询中有should的组合查询应注意minimum_should_match
  17. android 仿ios带弹簧效果的ScrollView
  18. <sub>和<sup>标签
  19. Pycharm 注册码
  20. 新能源汽车,视频云及半导体产业链市场格局分析

热门文章

  1. 调用支付宝接口开发遇到的错误
  2. [转]陈军:多规合一的信息平台解决方案
  3. [STM32学习]——一文搞懂I2C总线
  4. PTA——选择法排序
  5. html5二维动画教程,H5+JS二维动画制作的一个实例
  6. 080 反常积分(广义积分)
  7. 嵌入式ARM系统实战开发教程下载
  8. Linux——4linux实用操作
  9. redis漏洞利用总结
  10. 如何将一句英语句子的单词倒置