非典型算法題,用程序和電腦玩一個遊戲

你們好,歡迎閱讀週末算法題專題。算法

今天選擇的算法題是來自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 -
相關文章
相關標籤/搜索