非典型算法题,用程序和电脑玩一个游戏

你们好,欢迎阅读周末算法题专题。算法

今天选择的算法题是来自contest 1407比赛的C题,这题全场经过6700余人。经过的人数虽然多,可是这题真的不简单,想出算法来不太容易。抛开难度不提,这道题很是很是有意思,老实说这种形式的题目我也是第一次见。markdown

题目连接:https://codeforces.com/contest/1407/problem/Coop

废话很少说,咱们来看题目。spa

题意

这题是一道非典型的算法题,与其说是一道算法题不如说更像是一个游戏。游戏的目的是猜一个1至n这n个数构成的排列,咱们须要经过输入和输出和系统进行交互,从系统处获取更多的信息,最终给出猜想的结果。3d

首先系统会给定一个整数n,表示这个排列由n个数字构成,这n个数字由前n个正整数构成,也就是1到n这n个数字。以后咱们能够经过输出一个命令的形式向系统进行提问,提问的方式是? x y。系统会计算排列当中第x个数对第y个数取模的结果进行返回(排列的下标从1开始),也就是返回的值。code

系统最多接受2n次询问,当咱们已经猜出整个排列以后,输出这个排列。其中orm

样例

这个样例要倒过来看,第一个输入的3表示n。接着就先看输出再看输入。这个样例要猜的结果是[1, 3, 2],首先询问了num[1]对num[2]取余的结果,系统返回是1。接着询问num[3]对num[2]取余的结果,答案是2。接着询问num[1]%num[3]和num[2]%num[1],获得的结果分别是1和0。最终咱们根据这些信息猜想出了这个排列是[1, 3, 2],经过! 1 3 2的形式进行返回。token

思路

那道题以后咱们首先能够发现,n肯定了以后这n个数也就肯定了。由于n最大,其余数对n取模都是它自己。因此咱们须要先找到n的位置。游戏

可是n的位置并很差找,想来想去只有一种办法,就是当出现两个数的余数是n-1的时候,就能够肯定这两个数一个是n-1另一个是n。可是很明显,这样作咱们没法在规定步数内解出来。由于n个数两两组合一共有接近种,可是题目限定咱们最多只能询问2n次。get

很显然,先找到n再寻找其余值是不行的。

既然这个想法不行,我又开始找起了其余方法。咱们求了x % y的结果以后,究竟有什么用呢?这个结果到底有没有其余信息呢?

咱们来简单分析一下,咱们假设x % y = 1,那么这能说明什么吗?很明显,不能说明什么,由于可能性太多了。1对其余数取模都等于1,x % (x-1)也等于1。但假如x % y > (n/2) 呢?其实就能说明问题了。由于x和y的范围是[1, n],如今两个数取模以后的结果大于n的一半,很明显说明x就是这个结果,y是比x更大的数。还有一种状况是x % y = 0,这种状况咱们虽然没法肯定x和y的值,可是能够知道x必定是y的倍数且x > y。

虽然知道这些,但仍是不够解开问题,仍然须要碰运气,由于咱们并不能保证在查询次数内恰好就能够找到全部比二分之n大的数。可是这一段分析并非无用的,咱们能够在此基础上更进一步。咱们求了x和y的余数以后能够再求一下y和x的余数,假设x % y = a, y % x = b,经过分析a和b咱们可以获得什么结果呢?

首先咱们能够确定a和b不会相等,缘由也很简单,由于x和y必定不等(排列中的数各不相同)。咱们不妨假设x > y,那么y % x =b = y,x % y = a < y。若是a和b相等的话,那么就有 y < y,这显然是不成立的。因此能够确定a和b必定不等。其次从上面的证实咱们也看得出来,在x > y时,那么必定能够获得a < b。由于a = x % y < y,b = y % x = y。也就是说咱们能够经过a和b的关系判断x和y的关系,不只如此,还能够肯定y的值。

到这里距离解出这道题已经很是接近了,只差临门一脚,可是这里我仍是走了弯路。我当时的想法是把这些数两两配对,这样就能够肯定出其中的一半。以后咱们再把解不出来的数再进行配对,直到最后只剩下一个数为止。后来发现也有反例,好比[1, 3, 2, 4, 5],在这个例子当中1和3配对,2和4配对,5落单。咱们仍是没办法解出来。

我在这里苦思冥想了好久,后来才发现答案远在天边近在眼前,解法其实很是简单。咱们只须要从前日后遍历维护一个最大值便可。咱们假设最大值是id,凡是遇到小于id的数,均可以经过它和id取模的结果求出来。若是遇到了比id更大的数,一样能够经过取模的结果求出id。因此到最后的时候,咱们能够求出除了最大值其余的全部数,这个剩下的数就是n。

想通了真的很是很是简单,说穿了一钱不值,可是要靠本身想明白仍是不太容易的。而且这道题的题目形式也很新颖,很是很是有趣,适合在周末一玩。

最后,咱们来看代码:

import sys
  def guess(x, y):  print('? {} {}'.format(x, y))  # 输出以后须要flush一下,防止影响输入  sys.stdout.flush()  st = input()  return int(st)   st = input()  n = int(st) num = [-1 for _ in range(n+2)]  # 一开始将最大值的下标设为1 idx = 1 for i in range(2, n+1):  x = guess(idx, i)  y = guess(i, idx)  # 说明遇到了更大的数,那么x就是以前的最大值  if x > y:  num[idx] = x  idx = i  # 不然求出来的就是i  else:  num[i] = y  num[idx] = n  print('! {}'.format(' '.join(map(str, num[1:n+1])))) 复制代码

今天的问题到这里就结束了,衷心祝愿你们天天都有所收获。若是还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发

原文连接,求个关注

本文使用 mdnice 排版

- END -
相关文章
相关标签/搜索