题目描述

一张n*m的地图,每个格子里面有一定数量的神奇宝贝,求一个最优位置,使得所有神奇宝贝到该位置的曼哈顿距离最小。

一共有T组数据,每组数据包含两行,第一行是n和m(1<=n,m<=2000),第二行是三个整数x,y,q,表示处于(i,j)(1<=i<=n,1<=j<=m)的神奇宝贝的数量为((x^i) + (y^j))mod q,其中1<=x,y<=2000,1<=q<=10。

输出最小的曼哈顿距离和。

解题思路

这道题与2015编程之美挑战赛的基站选址类似(其实可以说是简单版),当时我没有做出来,给个地址吧:http://hihocoder.com/problemset/problem/1150

分析题目可以发现,由于是求曼哈顿距离,所以横行和竖行可以分开考虑,于是问题就变成了求加权平均数的问题。

公式:ans_x = ∑(ai*i) / ∑ai,ans_y = ∑(bj*j) / ∑bj,其中ai表示第i行的神奇宝贝数量,bi表示第i列的神奇宝贝数量,当然有∑ai == ∑bj。

这里有两点需要注意:

1、当结果不是整数时,涉及到向上取整还是向下取整的问题,由于与所有非零点的分布有关,所以最简单的办法是有可能成为答案的四个点都做一次判断就好了。

2、这里涉及到除法,所以可能会出现分母为零的情况,即地图上木有神奇宝贝【o(≧口≦)o摔桌子】,所以要特判…… 我才不会说我因为这个wa了好久QAQ

附:c++代码

(代码中有被注释掉的高精度的部分,因为我之前对数据规模的估计出错了…… 目前的高精度的代码还有错误,所以借鉴需要注意……)

时间复杂度O(n^2)

  1 #include <iostream>
2 #include <cstdio>
3 #include <cmath>
4
5 using namespace std;
6 #define MaxN 2020
7 #define MLen 14
8
9 typedef int hp[MLen];
10 typedef long long llt;
11
12 llt H[MaxN], L[MaxN];
13 int n, m;
14
15 inline void Add(hp a, hp b, hp &Ret)
16 {
17 int i, Len = max(a[0], b[0]) + 1;
18 hp c;
19 for(i = 1; i <= Len; i++)
20 c[i] = 0;
21 for(i = 1; i <= Len; i++)
22 {
23 c[i] += a[i] + b[i];
24 c[i + 1] += c[i] / 10;
25 c[i] %= 10;
26 }
27 while(Len > 1 && c[Len] == 0)
28 Len--;
29 c[0] = Len;
30 for(i = 0; i <= Len; i++)
31 Ret[i] = c[i];
32 }
33
34 inline void Add_Num(hp a, int b, hp &Ret)
35 {
36 int i, Len = a[0] + 1;
37 hp c;
38 for(i = 1; i <= Len - 1; i++)
39 c[i] = a[i];
40 c[i] = 0;
41 c[1] += b;
42 for(i = 1; i <= Len; i++)
43 {
44 c[i + 1] += c[i] / 10;
45 c[i] %= 10;
46 }
47 while(Len > 1 && c[Len] == 0)
48 Len--;
49 c[0] = Len;
50 for(i = 0; i <= Len; i++)
51 Ret[i] = c[i];
52 }
53
54 inline void Mul_Num(hp a, int b, hp &Ret)
55 {
56 int i, Len = a[0] + 5;
57 hp c;
58 for(i = 1; i <= a[0]; i++)
59 c[i] = a[i] * b;
60 for(i = a[0] + 1; i <= Len; i++)
61 c[i] = 0;
62 for(i = 1; i <= Len; i++)
63 {
64 c[i + 1] += c[i] / 10;
65 c[i] %= 10;
66 }
67 while(Len > 1 && c[Len] == 0)
68 Len--;
69 c[0] = Len;
70 for(i = 0; i <= Len; i++)
71 Ret[i] = c[i];
72 }
73
74 inline bool Check(hp a, hp b) // a<b
75 {
76 if(a[0] < b[0])
77 return true;
78 if(a[0] > b[0])
79 return false;
80 for(int i = a[0]; i >= 1; i--)
81 if(a[i] < b[i])
82 return true;
83 else if(a[i] > b[i])
84 return false;
85 return true;
86 }
87
88 inline int Chu(hp a, hp b) // a/b
89 {
90 int Low = 0, High = 2020, Mid;
91 hp tmp;
92 int op;
93 while(Low < High) //[,)
94 {
95 Mid = Low + (High - Low) / 2;
96 Mul_Num(b, Mid, tmp);
97 if(Check(tmp, a)) //tmp < a
98 {
99 Low = Mid + 1;
100 op = 0;
101 }
102 else
103 {
104 High = Mid;
105 op = 1;
106 }
107 }
108 if(!op)
109 return Low - 1;
110 else
111 return High - 1;
112 }
113
114 inline void Cal_Dis(int ph, int pl, llt &Ret)
115 {
116 int i, j;
117 //hp Ans;
118 llt Ans = 0;
119 //Ans[0] = 1; Ans[1] = 0;
120 for(i = 1; i < ph; i++)
121 //Add_Num(Ans, (ph - i) * H[i], Ans);
122 Ans += (llt)(ph - i) * H[i];
123 for(i = ph + 1; i <= n; i++)
124 //Add_Num(Ans, (i - ph) * H[i], Ans);
125 Ans += (llt)(i - ph) * H[i];
126 for(j = 1; j < pl; j++)
127 //Add_Num(Ans, (pl - j) * L[j], Ans);
128 Ans += (llt)(pl - j) * L[j];
129 for(j = pl + 1; j <= m; j++)
130 //Add_Num(Ans, (j - pl) * L[j], Ans);
131 Ans += (llt)(j - pl) * L[j];
132 //for(i = 0; i <= Ans[0]; i++)
133 // Ret[i] = Ans[i];
134 Ret = Ans;
135 }
136
137 inline void Print(hp a)
138 {
139 for(int i = a[0]; i >= 1; i--)
140 printf("%d", a[i]);
141 printf("\n");
142 }
143
144 int main()
145 {
146 int T;
147 int x, y, q;
148 int i, j, Num;
149 llt Ans, tmp;
150 //hp Sum_h, Sum_l, Psum_h, Psum_l;
151 llt Sum_h, Sum_l, Psum_h, Psum_l;
152 double ph, pl;
153 scanf("%d", &T);
154 while(T--)
155 {
156 scanf("%d%d", &n, &m);
157 scanf("%d%d%d", &x, &y, &q);
158 Psum_h = Psum_l = Sum_h = Sum_l = 0;
159 //Psum_h[0] = Psum_l[0] = Sum_h[0] = Sum_l[0] = 1;
160 //Psum_h[1] = Psum_l[1] = Sum_h[1] = Sum_l[1] = 0;
161 for(j = 1; j <= m; j++)
162 L[j] = 0;
163 for(i = 1; i <= n; i++)
164 {
165 H[i] = 0;
166 for(j = 1; j <= m; j++)
167 {
168 Num = ((x ^ i) + (y ^ j)) % q;
169 H[i] += Num;
170 L[j] += Num;
171 }
172 Sum_h += H[i];
173 Psum_h += (llt)i * H[i];
174 //Add_Num(Sum_h, H[i], Sum_h);
175 //Add_Num(Psum_h, i * H[i], Psum_h);
176 }
177 for(j = 1; j <= m; j++)
178 {
179 Sum_l += L[j];
180 Psum_l += (llt)j * L[j];
181 //Add_Num(Sum_l, L[j], Sum_l);
182 //Add_Num(Psum_l, j * L[j], Psum_l);
183 }
184 //ph = Chu(Psum_h, Sum_h);
185 //pl = Chu(Psum_l, Sum_l);
186 if(!Sum_h) //Sum_h == Sum_l
187 {
188 printf("0\n");
189 continue;
190 }
191 ph = Psum_h / Sum_h;
192 pl = Psum_l / Sum_l;
193 Cal_Dis(ph, pl, Ans);
194 Cal_Dis(ph + 1, pl, tmp);
195 //if(Check(tmp, Ans))
196 //for(i = 0; i <= tmp[0]; i++)
197 // Ans[i] = tmp[i];
198 if(tmp < Ans)
199 Ans = tmp;
200 Cal_Dis(ph, pl + 1, tmp);
201 //if(Check(tmp, Ans))
202 //for(i = 0; i <= tmp[0]; i++)
203 // Ans[i] = tmp[i];
204 if(tmp < Ans)
205 Ans = tmp;
206 Cal_Dis(ph + 1, pl + 1, tmp);
207 //if(Check(tmp, Ans))
208 //for(i = 0; i <= tmp[0]; i++)
209 // Ans[i] = tmp[i];
210 if(tmp < Ans)
211 Ans = tmp;
212 printf("%lld\n", Ans);
213 //Print(Ans);
214 }
215 return 0;
216 }

另一种思路

这是官方给的题解,没太明白O(n)是怎样实现的。

题目链接:https://biancheng.love/contest-ng/index.html#/29/problems

[题解]第十一届北航程序设计竞赛预赛——I.神奇宝贝大师的更多相关文章

  1. [题解]第十一届北航程序设计竞赛预赛——L.偶回文串

    题目描述 长度为偶数的回文串被称为偶回文串.如果一个字符串重新排序之后能够成为一个偶回文串,则称为可回文的. 给一个字符串,求可回文的子串个数.字符串只含小写字母,单个字符串长度不超过10^5,所有数 ...

  2. [题解]第十一届北航程序设计竞赛预赛——H.高中数学题

    题目描述 解题思路 可以求得通项公式:an = 2n + 1,所以问题就变成等差数列求异或和,这个具体为什么对我还不能很好地解释清楚,先挖坑吧. 附:c++代码 1 #include <iost ...

  3. [题解]第十一届北航程序设计竞赛预赛——F.序列

    题目描述 (1,--,n)的一个排列S,定义其对应的权值F[S]为:将S划分为若干段连续子序列,每个子序列都是上升序列,F[S]的值等于能划分出的最小段数. 求n的全排列的F[S]的和,答案mod(1 ...

  4. [题解]第十一届北航程序设计竞赛预赛——D.最大公约数

    题目描述 给一个长度为n(1<=n<=100000)的正整数列,分成尽量多的非空段,使得每一段的最大公约数相等.一个数的最大公约数是它本身. 解题思路 要求每一段子列的gcd相等,不妨设为 ...

  5. [题解]第十一届北航程序设计竞赛预赛——A.模式

    题目描述 输入一个学号,判断是计算机系or软件学院or其他院系. 解题思路 水题,直接判断or除以10000都可以.不废话,直接上代码. 1 #include <iostream> 2 # ...

  6. B P5 第十三届北航程序设计竞赛预赛

    https://buaacoding.cn/contest-ng/index.html#/188/problems 其实这题挺简单的. 注意到答案的大小最多是22 二分,check长度是mid的不同子 ...

  7. 湖南省第十一届大学生程序设计竞赛:Internet of Lights and Switches(HASH+二分+异或前缀和)

    Internet of Lights and Switches Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 3  Solved: 3[Submit][ ...

  8. 重庆邮电大学第十一届ACM程序设计竞赛-网络选拔赛 C题

    1008: 偷袭 时间限制: 1.000 sec 内存限制: 128 MB 武林要以和为贵,张麻子不讲武德来偷袭马老师的亲传弟子. 马老师有n个亲传弟子,每个弟子有一个武力值a[i]. n个弟子中只有 ...

  9. 河南省第十一届ACM程序设计竞赛 修路

    Problem C: 修路 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 63  Solved: 22[Submit][Status][Web Boar ...

随机推荐

  1. Cesium入门7 - Adding Terrain - 添加地形

    Cesium入门7 - Adding Terrain - 添加地形 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com ...

  2. java-异常-自定义异常异常类的抛出throws

    1 package p1.exception; 2 /* 3 * 对于角标是整数不存在,可以用角标越界表示, 4 * 对于负数为角标的情况,准备用负数角标异常来表示. 5 * 6 * 负数角标这种异常 ...

  3. python30day

    内容回顾 tcp协议的多人多次通信 和一个人通信多说句话 和一个人聊完再和其他人聊 bind 绑定一个id和端口 socket()tcp协议的server listen 监听,代表socket服务的开 ...

  4. JavaScript数据结构之链表

    链表相较于数组的优缺点 1. 链表在 插入.删除.移动数据效率比数组要高,数组插入.移动.删除数据需要改变没有数据的索引,而链表则只需要更改指针即可 2. 在查询方面,数组要优于链表,数组存储的数据是 ...

  5. Vue.js开发环境配置与项目创建

    一.需要安装和配置 Node.js 与 npm 二.Vue.js的安装或cdn引用: ·cdn引用(不适合项目开发): <script src="https://cdn.jsdeliv ...

  6. oracle中的常用函数、字符串函数、数值类型函数、日期函数,聚合函数。

    一.字符串的常用函数. --一.oracle 字符串常用函数 --1. concat 连接字符串的函数,只能连接[两个]字符串. 字符写在括号中,并用逗号隔开! --2."||"符 ...

  7. 羽夏看Win系统内核——同步篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  8. Sklearn 与 TensorFlow 机器学习实用指南第二版

    零.前言 一.机器学习概览 二.一个完整的机器学习项目 三.分类 四.训练模型 五.支持向量机 六.决策树 七.集成学习和随机森林 八.降维 十.使用 Keras 搭建人工神经网络 十一.训练深度神经 ...

  9. elasticsearch查询之大数据集分页性能测试

    一.测试环境 python 3.7 elasticsearch 6.8 elasticsearch-dsl 7 安装elasticsearch-dsl pip install elasticsearc ...

  10. AT2402 [ARC072D] Dam

    首先我们可以将 \(t_i \times v_i\) 看作一个整体,不妨令 \(x_i = v_i, y_i = t_i \times v_i\) 这样两堆水混合后相当于将两个维度相加,方便了计算. ...