[题解]第十一届北航程序设计竞赛预赛——I.神奇宝贝大师

题目描述

一张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

时间: 2024-10-06 03:27:49

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

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

题目描述 解题思路 可以求得通项公式:an = 2n + 1,所以问题就变成等差数列求异或和,这个具体为什么对我还不能很好地解释清楚,先挖坑吧. 附:c++代码 1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 typedef unsigned long long llt; 7 8 llt Cal(llt x, llt d, llt P, llt Num) 9 { 10 llt ret =

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

题目描述 给一个长度为n(1<=n<=100000)的正整数列,分成尽量多的非空段,使得每一段的最大公约数相等.一个数的最大公约数是它本身. 解题思路 要求每一段子列的gcd相等,不妨设为d,可以知道d是所有数的最大公约数,即d=(a[1],a[2],……,a[n]).于是我们先求出d,然后从前往后扫描,记st=1,移动ed,计算d1=(a[st],……,s[ed]),直到d1==d,得到的区间[st,ed]就是一个符合题目要求的子列:记st=ed+1,重复上述操作,直至数列扫完.算法时间复杂

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

题目描述 输入一个学号,判断是计算机系or软件学院or其他院系. 解题思路 水题,直接判断or除以10000都可以.不废话,直接上代码. 1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 6 int main() 7 { 8 char s[20]; 9 while(scanf("%s", s) != EOF) 10 { 11 if(s[2] == '0' &&

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

Internet of Lights and Switches Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 3  Solved: 3[Submit][Status][Web Board] Description You are a fan of "Internet of Things"(IoT, 物联网), so you build a nice Internet of Lights and Switches in your huge

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

Problem C: 修路 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 63  Solved: 22[Submit][Status][Web Board] Description SNJ位于HB省西部一片群峰耸立的高大山地,横亘于A江.B水之间,方圆数千平方公里,相传上古的神医在此搭架上山采药而得名.景区山峰均在海拔3000米以上,堪称"华中屋脊".SNJ是以秀绿的亚高山自然风光,多样的动植物种,人与自然和谐共存为主题的森林生态区. S

“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 (部分题解)

“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 F 自动售货机 时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte总提交 : 60            测试通过 : 13  题目描述 教学楼有一台奇怪的自动售货机,它只售卖一种饮料,单价5元,并且只收5元.10元面值的货币,但是同学们都很喜欢喝.这个售货机里没有多余的找零,也就是说如果一个持有10元的同学第一个购买,则他不能获得5元找零,但是如果在他之前有一个持有5

POJ 3420 Quad Tiling 题解 《挑战程序设计竞赛》

POJ 3420 Quad Tiling贴瓷砖:4*N的地板上用2*1的瓷砖铺满,求所有方案数对M求余.3.4熟练掌握动态规划矩阵的幂久违地上了节课,太无聊,只好刷一题.假设S[n]表示填满n时的方案数,有S[0]=1.定义矩阵M[p][q] := 边缘p和边缘q可以拼合时取1,否则取0所谓的可以拼合表示,两个边缘拼起来后长度为1(为2就拼接不起来了),且内部缝隙可以用2*1的瓷砖填满.那么M就有一些简单的性质了,比如M的第一行应该是:0 0 0 0 0 0... 继续阅读:码农场 » POJ

POJ 3411 Paid Roads 题解 《挑战程序设计竞赛》

POJ 3411 Paid Roads开路:N个城市间有m条单向路,分别从a到b,可以在c处交P路费,也可以直接交R路费.那么问题来了,你的挖掘机怎么开最省钱?3.4熟练掌握动态规划状态压缩DP乍一看可以Dijkstra,实际上的确可以Dijkstra.不过多了一个预交费的c,所以在遍历的时候多了一维状态,这个维度储存当前走过的城市集合.在Dijkstra的时候,如果走过了c那么就有两个选择,选其中最省的即可:否则没得选.#include <iostream> #include&nb.

《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就是一本题解书,简单暴力的通过题目的堆叠来提升解决编程问题的能力. 那么下面开始探索吧. zoj1037: BackgroundFor years, computer scientists have been trying to find efficient solutions to different computing p