非典型演算法題,用程式和電腦玩乙個遊戲

2021-10-24 22:32:09 字數 3135 閱讀 6040

今天選擇的演算法題是來自contest 1407比賽的c題,這題全場通過6700餘人。通過的人數雖然多,但是這題真的不簡單,想出演算法來不太容易。拋開難度不提,這道題非常非常有意思,老實說這種形式的題目我也是第一次見。

廢話不多說,我們來看題目。

這題是一道非典型的演算法題,與其說是一道演算法題不如說更像是乙個遊戲。遊戲的目的是猜乙個1至n這n個數構成的排列,我們需要通過輸入和輸出和系統進行互動,從系統處獲取更多的資訊,最終給出猜測的結果。

首先系統會給定乙個整數n,表示這個排列由n個數字構成,這n個數字由前n個正整數構成,也就是1到n這n個數字。之後我們可以通過輸出乙個命令的形式向系統進行提問,提問的方式是? x y。系統會計算排列當中第x個數對第y個數取模的結果進行返回(排列的下標從1開始),也就是返回num

[x]%

num[

y]num[x] \% num[y]

num[x]

%num

[y]的值。

系統最多接受2n次詢問,當我們已經猜出整個排列之後,輸出這個排列。其中n≤1

這個樣例要倒過來看,第乙個輸入的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的形式進行返回。

那道題之後我們首先可以發現,n確定了之後這n個數也就確定了。因為n最大,其他數對n取模都是它本身。所以我們需要先找到n的位置。

但是n的位置並不好找,想來想去只有一種辦法,就是當出現兩個數的餘數是n-1的時候,就可以確定這兩個數乙個是n-1另外乙個是n。但是很明顯,這樣做我們無法在規定步數內解出來。因為n個數兩兩組合一共有接近n

2n^2

n2種,但是題目限定我們最多只能詢問2n次。

很顯然,先找到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

defguess

(x, y)

:print

('? {} {}'

.format

(x, y)

)# 輸出之後需要flush一下,防止影響輸入

sys.stdout.flush(

) st =

input()

return

int(st)

st =

input()

n =int

(st)

num =[-

1for _ in

range

(n+2)]

# 一開始將最大值的下標設為1

idx =

1for 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]

= ynum[idx]

= nprint

('! {}'

.format

(' '

.join(

map(

str, num[

1:n+1]

))))

用C 寫乙個UDP傳送和接收程式

用c 寫乙個udp傳送和接收程式 2011 03 18 13 10 16 分類 c c 傳送程式sender.cpp include include include include using namespace std 建立新的套接字之前需要呼叫乙個引入ws2 32.dll庫的函式,否則伺服器和客...

用兩個棧實現乙個佇列,演算法和實現

include include include using namespace std 用兩個棧實現乙個佇列,演算法和實現 class queue my queue my queue my void queue my createqueue const vector vec 入隊函式 演算法 使新入...

用兩個棧實現乙個佇列的功能?要求給出演算法和思路!

用兩個棧實現乙個佇列的功能?要求給出演算法和思路!堆疊的特徵 filo 先進後出 佇列的特徵 fifo 先進先出 所以,用兩個棧s1和s2模擬乙個佇列時,s1作輸入棧,逐個元素壓棧,以此模擬佇列元素的入隊。當需要出隊時,將棧s1退棧並逐個壓入棧s2中,s1中最先入棧的元素,在s2中處於棧頂。s2退棧...