容斥原理 博弈論(多種Nim游戲解法)

目錄

  • 容斥原理
    • 容斥原理的簡介
    • 能被整除的數(典型例題)
      • 實現思路
      • 代碼實現
      • 擴展:用DPS實現
  • 博弈論
    • 博弈論中的相關性質
    • 博弈論的相關結論
    • 先手必敗必勝的證明
    • Nim游戲(典型例題)
      • 代碼實現
    • 臺階-Nim游戲(典型例題)
      • 實現思路
      • 代碼實現
    • Mex函數與SG函數
    • 集合-Nim游戲(典型例題)
      • 代碼實現
    • 拆分-Nim游戲(典型例題)
      • 實現思路
      • 代碼實現


容斥原理

容斥原理的簡介

容斥原理是組合數學中的一個重要原理,討論了滿足某些條件的元素個數問題。

對于 n n n 個集合 A 1 , A 2 , . . . A n A_1,A_2,...A_n A1?,A2?,...An?,定義他們的并集包含的元素個數為 ∣ A 1 ∪ A 2 ∪ . . . ∪ A n ∣ |A_1∪A_2∪...∪A_n| A1?A2?...An?,那么根據容斥原理,有:

∣ A 1 ∪ A 2 ∪ ? ∪ A n ∣ |A_1 \cup A_2 \cup \dots \cup A_n| A1?A2??An?
= ∣ A 1 ∣ + ∣ A 2 ∣ + ? + ∣ A n ∣ = |A_1| + |A_2| + \dots + |A_n| =A1?+A2?+?+An?
? ∣ A 1 ∩ A 2 ∣ ? ? ? ∣ A n ? 1 ∩ A n ∣ - |A_1 \cap A_2| - \dots - |A_{n-1} \cap A_n| ?A1?A2????An?1?An?
+ ∣ A 1 ∩ A 2 ∩ A 3 ∣ + ? + ( ? 1 ) n + 1 ∣ A 1 ∩ A 2 ∩ ? ∩ A n ∣ + |A_1 \cap A_2 \cap A_3| + \dots + (-1)^{n+1} |A_1 \cap A_2 \cap \dots \cap A_n| +A1?A2?A3?+?+(?1)n+1A1?A2??An?

(奇數項為正 偶數項為負)

例如:
在這里插入圖片描述
根據上圖可得
∣ A ∪ B ∪ C ∣ = ∣ A ∣ + ∣ B ∣ + ∣ C ∣ ? ∣ A ∩ B ∣ ? ∣ B ∩ C ∣ ? ∣ A ∩ C ∣ + ∣ A ∩ B ∩ C ∣ |A \cup B \cup C|= |A|+|B|+|C|-|A \cap B| - |B \cap C| - |A \cap C| + |A \cap B \cap C| ABC=A+B+C?AB?BC?AC+ABC

對于 n n n 個集合 A 1 , A 2 , . . . A n A_1,A_2,...A_n A1?,A2?,...An?,其項數可以表示為 C n 1 + C n 2 + ? + C n n C_n^1 + C_n^2+\dots+C_n^n Cn1?+Cn2?+?+Cnn? 通過數學計算可得 C n 1 + C n 2 + ? + C n n = 2 n ? 1 C_n^1 + C_n^2+\dots+C_n^n = 2^n - 1 Cn1?+Cn2?+?+Cnn?=2n?1,用此再乘以每項的時間復雜度便可以得到整體時間復雜度。

另外也可以通過數學計算得到 C n 1 ? C n 2 + ? + ( ? 1 ) n ? 1 C n n = 1 C_n^1 - C_n^2+\dots+(-1)^{n-1}C_n^n = 1 Cn1??Cn2?+?+(?1)n?1Cnn?=1


能被整除的數(典型例題)

給定一個整數 n n n m m m 個不同的質數 p 1 , p 2 , … , p m p_1,p_2,\dots,p_m p1?,p2?,,pm?

請你求出 1 1 1 ~ n n n 中能被 p 1 , p 2 , … , p m p_1,p_2,\dots,p_m p1?,p2?,,pm? 中的至少一個數整除的整數有多少個。

輸入格式:
第一行包含整數 n n n m m m

第二行包含 m m m 個質數

輸出格式:
輸出一個整數,表示滿足條件的整數的個數。

數據范圍:
1 ≤ m ≤ 16 1 \leq m \leq 16 1m16
1 ≤ n , p i ≤ 1 0 9 1 \leq n,p_i \leq 10^9 1n,pi?109

輸入樣例:

10 2
2 3

輸出樣例:

7

實現思路

計算 1 1 1 ~ n n n 中能被 p i p_i pi? 一個數整除的整數的方法為 ? n p i ? \huge \lfloor \frac{n}{p_i} \rfloor ?pi?n??

由于不同質數間存在相同的可被整除的整數情況,這時候直接計算就會導致重疊部分計算多次,因此需要將重復的部分剔除出去,這里使用容斥原理來進行剔除操作。

計算 1 1 1 ~ n n n 中能被 p i , p j , p k p_i,p_j,p_k pi?,pj?,pk? 多個數整除的整數(類似于重疊部分)的方法為 ? n p i ? p j ? p k ? \huge \lfloor \frac{n}{p_i*p_j*p_k} \rfloor ?pi??pj??pk?n??

m m m 個質數看作 m m m 個集合, 由于 m m m 較小,我們可以使用int類型的二進制形式(32 bit)的變量來表示對每個集合的選取情況,1代表選用,0代表未選用。

比如:1110 可看作為選用集合 1 2 3,而集合 0 未選用,這樣便可以通過一個int類型遍歷來表示當前的集合選用情況。

通過二進制數模擬所有集合選用情況,再利用遍歷對二進制數進行解碼操作,而后進行正負性相加減便可以得到最終結果。


代碼實現

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;const int N = 20;
int p[N];int main()
{int n, m, res = 0;cin >> n >> m;for (int i = 0; i < m; ++i) cin >> p[i];// 模擬對所有集合的每一種選用情況for (int i = 1; i < 1 << m; ++i) // 從1開始模擬{int t = 1, cnt = 0;for (int j = 0; j < m; ++j) // 將二進制數所表示的選用情況提取出來(解碼){if (!(i >> j & 1)) continue;if ((long long)t * p[j] > n)// 當質數間的乘積大于n時,對應的質數重疊情況只有0個{t = -1;break;}t *= p[j];cnt++;}if (t != -1){int sign = (cnt & 1) ? 1 : -1; // 判斷奇偶性res = res + n / t * sign;}}cout << res << endl;return 0;
}

時間復雜度: O ( 2 m ? m ) O(2^m*m) O(2m?m)

通過公式 C n 1 + C n 2 + ? + C n n = 2 n ? 1 C_n^1 + C_n^2+\dots+C_n^n = 2^n - 1 Cn1?+Cn2?+?+Cnn?=2n?1 可得遍歷所有項的時間復雜度為 O ( 2 m ) O(2^m) O(2m), 對單獨一項的時間復雜度為 O ( m ) O(m) O(m),這是因為使用二進制數來表示情況,所以需要時間復雜度為 O ( m ) O(m) O(m) 的遍歷來進行解碼操作,提取其中的選取信息。


擴展:用DPS實現

const int N = 20;
int n, m, p[N], res;// num-代表當前質數乘積 
// cur-代表指向質數數組的指針
// cnt-代表所選用集合的總數
void dfs(int num, int cur, int cnt)
{if (cur == m) // 當cur把質數數組都遍歷一遍之后代表該種選用情況的結束{if (cnt) // 排除一個質數都不選的選用情況{if (cnt % 2) res += n / num;else res -= n / num;}return;}dfs(num, cur + 1, cnt); // 不選用當前質數的集合dfs(num * p[cur], cur + 1, cnt + 1); // 選用當前質數的集合
}
int main()
{cin >> n >> m;for (int i = 0; i < m; ++i) cin >> p[i];dfs(1, 0, 0);cout << res << endl;return 0;
}

時間復雜度: O ( 2 m ) O(2^m) O(2m)

由于不用二進制數來模擬選取情況,所以也就不需要時間復雜度為 O ( m ) O(m) O(m) 的遍歷來從二進制數中提取出選取情況的解碼操作。


博弈論

博弈論(Game Theory) 是研究 多方參與者 在某種競爭或合作情況下進行的策略選擇相互作用的一門數學理論。它主要研究參與者如何通過各種策略最大化自己的收益。


博弈論中的相關性質

公平組合游戲(Impartial Combinatorial Game, ICG)是組合博弈論中的一個重要概念。

所謂公平組合游戲,是指具有以下特征:

  1. 游戲中只有兩個玩家,分先手和后手。
  2. 兩者地位完全對等,有同樣的可選行動。
  3. 每個位置要么先手必勝,要么后手必勝,不存在平局。
  4. 參與者都會采取最優策略。

注:城建棋類游戲,比如圍棋,就不是公平組合游戲。因為圍棋交戰雙方分別只能落黑子和白子,勝負判定也比較復雜,不符合公平組合游戲。

一些典型的公平組合游戲包括:

  • Nim游戲:從幾堆物品中拿走任意數量的游戲。
  • 威佐夫游戲:在圖上移動棋子吃掉對方的游戲。
  • 沒有后效應的游戲:當前行動不會對之后可行動產生影響。

通過對這類游戲規律的研究,發展出了一套完整的理論體系,建立了組合博弈論這一獨立的學科分支。公平組合游戲抽象化地反映了很多現實世界的競爭情況。


博弈論的相關結論

先手必勝狀態:

  • 在這個游戲狀態下,先手玩家有一個必勝策略,可以通過采取相應的游戲行動最終獲勝,不論對手如何行動。
  • 即先手玩家可以保證自己贏得游戲。這個游戲狀態對先手玩家有利。

先手必敗狀態:

  • 在這個游戲狀態下,先手玩家無論采取何種行動最終都會輸,后手玩家存在必勝策略。
  • 即該狀態下無論先手玩家如何行動,后手玩家都可以通過相應的行動獲取最終的勝利。這個狀態對后手玩家有利。
  • 后手玩家可以保證自己贏得游戲。

對于NIM游戲,在兩名選手都足夠聰明每一步都是最優解的情況下:

a 1 ⊕ a 2 ⊕ ? ⊕ a n = 0 a_1 \oplus a_2 \oplus \dots \oplus a_n = 0 a1?a2??an?=0 先手必敗

a 1 ⊕ a 2 ⊕ ? ⊕ a n ≠ 0 a_1 \oplus a_2 \oplus \dots \oplus a_n \neq 0 a1?a2??an?=0 先手必勝

就是把所有堆的石子個數異或起來,結果是零,先手必敗,不是零,先手必勝。


先手必敗必勝的證明

①當 a 1 ⊕ a 2 ⊕ ? ⊕ a n = 0 a_1 \oplus a_2 \oplus \dots \oplus a_n = 0 a1?a2??an?=0 時,先手必敗:

這個結論實際上是基于一個性質,即對于任意一個非負整數 x x x,有 x ⊕ x = 0 x \oplus x = 0 xx=0。基于這個性質,我們可以證明在這種情況下,先手必敗。

假設初始狀態下,有 a 1 ⊕ a 2 ⊕ ? ⊕ a n = 0 a_1 \oplus a_2 \oplus \dots \oplus a_n = 0 a1?a2??an?=0。考慮以下情況:

  • 如果先手選擇 a 1 a_1 a1?,那么剩下的異或和就變為 ( a 2 ⊕ ? ⊕ a n ) (a_2 \oplus \dots \oplus a_n) (a2??an?)
  • 如果先手選擇 a 2 a_2 a2?,那么剩下的異或和就變為 ( a 1 ⊕ a 3 ⊕ ? ⊕ a n ) (a_1 \oplus a_3 \oplus \dots \oplus a_n) (a1?a3??an?)
  • 以此類推,如果先手選擇 a i a_i ai?,剩下的異或和就變為 ( a 1 ⊕ ? ⊕ a i ? 1 ⊕ a i + 1 ⊕ ? ⊕ a n ) (a_1 \oplus \dots \oplus a_{i-1} \oplus a_{i+1} \oplus \dots \oplus a_n) (a1??ai?1?ai+1??an?)

在這些情況下,無論先手如何選擇,剩下的異或和都會變成一個非零值。由于異或滿足交換律和結合律,這些選擇的結果最終都會導致剩余的異或和為一個非零值。因此,無論先手如何操作,最終的游戲狀態都會變為一個不滿足條件的狀態。


②當 a 1 ⊕ a 2 ⊕ ? ⊕ a n ≠ 0 a_1 \oplus a_2 \oplus \dots \oplus a_n \neq 0 a1?a2??an?=0 時,先手必勝:

在這種情況下,異或和不為零。證明先手必勝需要使用歸納法。對于 n = 1 n=1 n=1的情況,只有一個數,先手直接取走即可。

假設對于 n = k n=k n=k,當 a 1 ⊕ a 2 ⊕ ? ⊕ a k ≠ 0 a_1 \oplus a_2 \oplus \dots \oplus a_k \neq 0 a1?a2??ak?=0 時,先手必勝。現在考慮 n = k + 1 n=k+1 n=k+1 的情況。

a 1 ⊕ a 2 ⊕ ? ⊕ a k + 1 a_1 \oplus a_2 \oplus \dots \oplus a_{k+1} a1?a2??ak+1? 分成兩部分: x = a 1 ⊕ a 2 ⊕ ? ⊕ a k x = a_1 \oplus a_2 \oplus \dots \oplus a_k x=a1?a2??ak? a k + 1 a_{k+1} ak+1?。根據歸納假設,當 x ≠ 0 x \neq 0 x=0 時,先手必勝。此時,先手可以執行以下操作:

  • 如果先手選擇取走 a k + 1 a_{k+1} ak+1?,那么剩下的異或和變為 x x x,根據歸納假設,先手必勝。
  • 如果先手選擇取走 a i a_i ai? 1 ≤ i ≤ k 1 \leq i \leq k 1ik),剩下的異或和變為 x ⊕ a i x \oplus a_i xai?。由于 x ≠ 0 x \neq 0 x=0 x ⊕ a i x \oplus a_i xai? 也不為零,那么根據歸納假設,先手也必勝。

因此,無論哪種情況,先手都有必勝策略。綜上所述,當 a 1 ⊕ a 2 ⊕ ? ⊕ a n ≠ 0 a_1 \oplus a_2 \oplus \dots \oplus a_n \neq 0 a1?a2??an?=0 時,先手必勝。


Nim游戲(典型例題)

題目描述:
給定 n n n 堆石子,兩位玩家輪流操作,每次操作可以從任意一堆石子中拿走任意數量的石子(可以拿完,但不能不拿),最后無法進行操作的人視為失敗。

問如果兩人都采用最優策略,先手是否必勝。

輸入格式:
第一行包含整數 n n n

第二行包含 n n n 個數字,其中第 i i i 個數字表示第 i i i 堆石子的數量。

輸出格式:
如果先手方必勝,則輸出 Yes

否則,輸出 No

數據范圍:
1 ≤ n ≤ 1 0 5 , 1 ≤ 1≤n≤10^5,1≤ 1n105,1每堆石子數 ≤ 1 0 9 ≤10^9 109

輸入樣例:

2
2 3

輸出樣例:

Yes

代碼實現

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;int main()
{int n, res = 0;cin >> n;for (int i = 0; i < n; ++i){int tmp;cin >> tmp;res ^= tmp;}if (res) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

臺階-Nim游戲(典型例題)

題目描述:
現在,有一個 n n n 級臺階的樓梯,每級臺階上都有若干個石子,其中第 i i i 級臺階上有 a i a_i ai? 個石子( i ≥ 1 i≥1 i1)。

兩位玩家輪流操作,每次操作可以從任意一級臺階上拿若干個石子放到下一級臺階中(不能不拿)。

已經拿到地面上的石子不能再拿,最后無法進行操作的人視為失敗。

問如果兩人都采用最優策略,先手是否必勝。

輸入格式:
第一行包含整數 n n n

第二行包含 n n n 個整數,其中第 i i i 個整數表示第 i i i 級臺階上的石子數 a i a_i ai?

輸出格式:
如果先手方必勝,則輸出 Yes

否則,輸出 No

數據范圍:
1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 9 1≤n≤10^5,1≤a_i≤10^9 1n105,1ai?109

輸入樣例:

3
2 1 3

輸出樣例:

Yes

實現思路

在這里插入圖片描述
模擬每次只能向下挪動至下一級臺階的行動,我的想法是將高于臺階1的堆分成多個堆,例如圖中臺階5的堆,將這個堆分為5份石子數量相等的臺階1的堆,模擬需要至少5次才能將臺階5的堆拿至地面。

因此可以得到: 2 ? 臺階 1 ⊕ 1 ⊕ 1 ? 臺階 2 ⊕ 3 ⊕ 3 ⊕ 3 ? 臺階 3 ⊕ 2 ⊕ 2 ⊕ 2 ⊕ 2 ? 臺階 4 ⊕ 4 ⊕ 4 ⊕ 4 ⊕ 4 ⊕ 4 ? 臺階 5 \underbrace{2}_{臺階1} \oplus \underbrace{1 \oplus 1}_{臺階2} \oplus \underbrace{3 \oplus 3 \oplus 3}_{臺階3} \oplus \underbrace{2 \oplus 2 \oplus 2 \oplus 2}_{臺階4} \oplus \underbrace{4 \oplus 4 \oplus 4 \oplus 4 \oplus 4}_{臺階5} 臺階1 2??臺階2 11??臺階3 333??臺階4 2222??臺階5 44444??

由于異或(xor)的特性,相同的數異或等于0,便可以進行優化,當階數為偶數時,異或結果必定為0,奇數時,異或結果直接就等于臺階上的石子數。

從而可以推導出結論:

  • 如果 奇數 階臺階的石子個數 異或值不是零,則 先手必勝

  • 如果 奇數 階臺階的石子個數 異或值是零, 則 先手必敗


代碼實現

未利用異或特性優化前:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;int main()
{int n, res = 0;cin >> n;for (int i = 1; i <= n; ++i){int tmp, cnt = i;cin >> tmp;while (cnt--)	res ^= tmp; // 由臺階數分堆}if (res) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

利用異或特性優化后:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;int main()
{int n, res = 0;cin >> n;for (int i = 1; i <= n; ++i){int tmp;cin >> tmp;if (i & 1) res ^= tmp; // 利用異或特性優化}if (res) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

Mex函數與SG函數

有向圖游戲的概念:

  • 節點和邊:有向圖游戲由一組節點和連接節點的有向邊組成。每個節點表示游戲的一個狀態,而有向邊表示從一個狀態到另一個狀態的合法轉移。

  • 玩家輪流移動:在有向圖游戲中,玩家輪流進行移動。每一步玩家可以從當前狀態轉移到某個后繼狀態,選擇的后繼狀態必須遵循游戲的規則。

  • 游戲終止狀態:每個有向圖游戲都有終止狀態,到達終止狀態后游戲結束。終止狀態可能是獲勝狀態或失敗狀態,這取決于游戲的規則。

  • SG 函數和 Mex 函數:SG 函數(Sprague-Grundy 函數)和 Mex 函數是解決有向圖游戲的關鍵工具。SG 函數為每個節點賦予一個非負整數值,表示該節點的狀態價值。Mex 函數返回集合中沒有出現的最小非負整數。通過 SG 函數和 Mex 函數,可以計算出游戲的最佳策略。

  • 必勝策略(先手必勝)和必敗策略(先手必敗):在有向圖游戲中,存在必勝策略和必敗策略。如果一個玩家始終能夠采取最佳策略,并且可以確保在有限步內獲勝,那么該玩家有必勝策略。相反,如果玩家無論如何都無法避免失敗,那么該玩家有必敗策略。

  • Nim 游戲和其他變體:Nim 游戲是有向圖游戲的一個著名例子,它涉及在堆中取石子。還有許多其他變體的有向圖游戲,如拈游戲、Grundy’s Game 等。


Mex(Minimum Excluded value)函數,是一個常見的組合游戲和集合論中的概念。它通常用來解決一類博弈問題,其中兩個玩家輪流從一個集合中取元素,每次取一個元素,并且不能重復取已經被取走的元素。當某一玩家無法繼續操作時,該玩家輸掉游戲。


Mex 函數的定義:對于一個給定的非負整數集合,它返回一個非負整數,表示在這個集合中,不屬于集合最小非負整數

mex ( S ) = min ? { x ∣ x ∈ N 0 , x ? S } \text{mex}(S) = \min \{ x \mid x \in \mathbb{N}_0, x \notin S \} mex(S)=min{xxN0?,x/S}

例如:

S = { 0 , 1 , 2 , 4 } ? m e x ( S ) = 3 S=\{0,1,2,4\} ? mex(S)=3 S={0,1,2,4}?mex(S)=3


m e x mex mex函數和 s g sg sg函數用于計算有向圖游戲的答案,主要思路是:

  • m e x mex mex函數返回集合 S S S 中沒有出現的最小非負整數。
  • s g sg sg函數定義為 s g ( n ) = m e x ( s g ( i 1 ) , s g ( i 2 ) . . . ) sg(n)=mex({sg(i_1),sg(i_2)...}) sg(n)=mex(sg(i1?),sg(i2?)...),其中 i 1 , i 2 . . . i_1,i_2... i1?,i2?... 是節點 n n n 的后繼節點。
  • 對于圖 G G G,定義 s g ( G ) = s g ( h e a d ) sg(G)=sg(head) sg(G)=sg(head),其中head是G的頭節點。
  • 對于 n n n 個圖 G 1 , G 2 , . . . G n G_1,G_2,...G_n G1?,G2?,...Gn?,它們的 s g sg sg函數值的異或和就是這個有向圖游戲的答案。

假設有 n n n 個獨立局面,它們的 S G SG SG 值分別為 s g ( G 1 ) , s g ( G 2 ) , . . . , s g ( G n ) sg(G1), sg(G2), ..., sg(Gn) sg(G1),sg(G2),...,sg(Gn),其中 G i G_i Gi? 表示第 i i i 個局面。那么整個組合游戲的 S G SG SG s g ( G ) sg(G) sg(G) 可以表示為這些局面 S G SG SG 值的異或和:

s g ( G ) = s g ( G 1 ) ⊕ s g ( G 2 ) ⊕ . . . ⊕ s g ( G n ) sg(G) = sg(G_1) \oplus sg(G_2) \oplus ... \oplus sg(G_n) sg(G)=sg(G1?)sg(G2?)...sg(Gn?)

例如:

以NIM游戲為例,數量為7個石子的一堆,每人抽取輪流只能抽取2或5個石子,不能不抽取,判斷先手必勝還是先手必敗。
在這里插入圖片描述
從末尾開始計算,可以得到 s g ( h e a d ) = 0 sg(head) = 0 sg(head)=0,由于此時只有一堆,即只存在一個有向圖,所以 s g ( h e a d ) = 0 sg(head) = 0 sg(head)=0 得到結果先手必敗。

對多個有向圖的情況(例如多個堆的情況),將每個有向圖的sg(head)異或到一起,再根據先手必勝必敗的條件,得出對應的結論。


G 1 , G 2 , G 3 , … , G n G_1,G_2,G_3,\dots,G_n G1?,G2?,G3?,,Gn? n n n 個有向圖,結論為:

先手必敗
s g ( G 1 ) ⊕ s g ( G 2 ) ⊕ s g ( G 3 ) ⊕ ? ⊕ s g ( G n ) = 0 sg(G_1) \oplus sg(G_2) \oplus sg(G_3) \oplus \dots \oplus sg(G_n) = 0 sg(G1?)sg(G2?)sg(G3?)?sg(Gn?)=0
先手必勝
s g ( G 1 ) ⊕ s g ( G 2 ) ⊕ s g ( G 3 ) ⊕ ? ⊕ s g ( G n ) ≠ 0 sg(G_1) \oplus sg(G_2) \oplus sg(G_3) \oplus \dots \oplus sg(G_n) \neq 0 sg(G1?)sg(G2?)sg(G3?)?sg(Gn?)=0


集合-Nim游戲(典型例題)

題目描述:
給定 n n n 堆石子以及一個由 k k k 個不同正整數構成的數字集合 S S S

現在有兩位玩家輪流操作,每次操作可以從任意一堆石子中拿取石子,每次拿取的石子數量必須包含于集合 S S S,最后無法進行操作的人視為失敗。

問如果兩人都采用最優策略,先手是否必勝。

輸入格式:
第一行包含整數 k k k,表示數字集合 S S S 中數字的個數。

第二行包含 k k k 個整數,其中第 i i i 個整數表示數字集合 S S S 中的第 i i i 個數 s i s_i si?

第三行包含整數 n n n

第四行包含 n n n 個整數,其中第 i i i 個整數表示第 i i i 堆石子的數量 h i h_i hi?

輸出格式:
如果先手方必勝,則輸出 Yes

否則,輸出 No

數據范圍:
1 ≤ n , k ≤ 100 , 1 ≤ s i , h i ≤ 10000 1≤n,k≤100,1≤s_i,h_i≤10000 1n,k100,1si?,hi?10000

輸入樣例:

2
2 3

輸出樣例:

Yes

代碼實現

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<unordered_set>
using namespace std;const int N = 110, M = 10010;
int a[N], f[M], k, n;int sg(int x)
{if (~f[x]) return f[x]; // 記憶化搜索:保證每種狀態只會被算一次unordered_set<int> s; // 用哈希表存儲當前節點的集合所存有的非負數的元素for (int i = 0; i < k; ++i){int num = a[i];if (x >= num) s.insert(sg(x - num)); // 將新的狀態加進來}for (int i = 0;; i++) if (!s.count(i)) return f[x] = i; // 找出不存在集合當中的最小值并返回
}
int main()
{int res = 0;memset(f, -1, sizeof f);cin >> k;for (int i = 0; i < k; ++i) cin >> a[i];cin >> n;for (int i = 0; i < n; ++i){int x;cin >> x;res ^= sg(x); // 求出每堆石子的SG值將其異或}if (res) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

拆分-Nim游戲(典型例題)

題目描述:
給定 n n n 堆石子,兩位玩家輪流操作,每次操作可以取走其中的一堆石子,然后放入兩堆 規模更小 的石子(新堆規模可以為 0 0 0,且兩個新堆的石子總數可以大于取走的那堆石子數),最后無法進行操作的人視為失敗。

問如果兩人都采用最優策略,先手是否必勝。

輸入格式:
第一行包含整數 n n n

第二行包含 n n n 個整數,其中第 i i i 個整數表示第 i i i 堆石子的數量 a i a_i ai?

輸出格式:
如果先手方必勝,則輸出 Yes

否則,輸出 No

數據范圍:
1 ≤ n , a i ≤ 100 1≤n,a_i≤100 1n,ai?100

輸入樣例:

2
2 3

輸出樣例:

Yes

實現思路

取走其中的一堆石子,然后放入兩堆 規模更小 的石子,相當于將一個局面拆分成了兩個局面,由SG函數理論:多個獨立局面的SG值,等于這些局面SG值的 異或和

s g ( G ) = s g ( G 1 ) ⊕ s g ( G 2 ) ⊕ . . . ⊕ s g ( G n ) sg(G) = sg(G_1) \oplus sg(G_2) \oplus ... \oplus sg(G_n) sg(G)=sg(G1?)sg(G2?)...sg(Gn?)


代碼實現

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<unordered_set>
using namespace std;const int N = 110, M = 10010;
int f[N], n;int sg(int x)
{if (~f[x]) return f[x];unordered_set<int> s;for (int i = 0; i < x; ++i){for (int j = 0; j <= i; ++j)s.insert(sg(i) ^ sg(j));}for (int i = 0;; i++) if (!s.count(i)) return f[x] = i;
}
int main()
{memset(f, -1, sizeof f);cin >> n;int res = 0;while (n--){int x;cin >> x;res ^= sg(x);}if (res) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42883.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42883.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42883.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

什么叫做云計算

什么叫做云計算 相信大多數人對云計算或者是云服務的認識還停留在僅僅聽過這個名詞&#xff0c;但是對其真正的定義或者意義還不甚了解的層面。甚至有些技術人員&#xff0c;如果日常的業務不涉及到云服務&#xff0c;可能對其也只是一知半解的程度。首先云計算準確的講只是云服…

Java多態詳解(1)

多態 多態的概念 所謂多態&#xff0c;通俗地講&#xff0c;就是多種形態&#xff0c;具體點就是去完成某個行為&#xff0c;當不同的對象去完成時會產生出不同的狀態。 比如&#xff1a; 這一時間爆火的“現代紀錄片”中&#xff0c;麥克阿瑟總是對各種“名人”有不同的評價&…

算法通關村第十關 | 歸并排序

1. 歸并排序原理 歸并排序&#xff08;MERARE-SORT&#xff09;簡單來說就是將大的序列先視為若干個比較小的數組&#xff0c;分成比較小的結構&#xff0c;然后是利用歸并的思想實現的排序方法&#xff0c;該算法采用經典的分治策略&#xff08;分就是將問題分成一些小的問題分…

【Axure模板】APP幫助中心原型,在線客服意見反饋模塊高保真原型

作品概況 頁面數量&#xff1a;共 10 頁 兼容軟件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 應用領域&#xff1a;原型設計模板 作品申明&#xff1a;頁面內容僅用于功能演示&#xff0c;無實際功能 作品特色 該模板作品為APP幫助與客服的通用模塊&#xff0c;…

golang操作excel的高性能庫——excelize/v2

目錄 介紹文檔與源碼安裝快速開始創建 Excel 文檔讀取 Excel 文檔打開數據流流式寫入 [相關 Excel 開源類庫性能對比](https://xuri.me/excelize/zh-hans/performance.html) 介紹 Excelize是一個純Go編寫的庫&#xff0c;提供了一組功能&#xff0c;允許你向XLAM / XLSM / XLS…

【Kubernetes】Kubernetes的Pod控制器

Pod控制器 一、Pod 控制器的概念1. Pod 控制器及其功用2. Pod 控制器有多種類型2.1 ReplicaSet2.2 Deployment2.3 DaemonSet2.4 StatefulSet2.5 Job2.6 Cronjob 3. Pod 與控制器之間的關系 二、Pod 控制器的使用1. Deployment2. SatefulSet2.1 為什么要有headless&#xff1f;2…

CF113A Grammar Lessons 題解

一道模擬題。 題目傳送門 題目意思&#xff1a; 給你一個句子&#xff0c;讓你檢查這個句子的語法是否正確。&#xff08;語法請自行在題目中查看&#xff09; 思路&#xff1a; 就是模擬。依次判斷這個句子是否符合每一條語法即可。但是細節很多就因為細節我錯了好多次&…

數據挖掘 | 零代碼采集房源數據,支持自動翻頁、數據排重等

1 前言 城市規劃、商業選址等應用場景中經常會對地區房價、地域價值進行數據分析&#xff0c;其中地區樓盤房價是分析數據中重要的信息參考點&#xff0c;一些互聯網網站上匯聚了大量房源信息&#xff0c;通過收集此類數據&#xff0c;能夠對地區房價的分析提供參考依據。 如何…

216、仿真-基于51單片機溫度煙霧人體感應布防報警Proteus仿真設計(程序+Proteus仿真+原理圖+配套資料等)

畢設幫助、開題指導、技術解答(有償)見文未 目錄 一、硬件設計 二、設計功能 三、Proteus仿真圖 四、原理圖 五、程序源碼 資料包括&#xff1a; 需要完整的資料可以點擊下面的名片加下我&#xff0c;找我要資源壓縮包的百度網盤下載地址及提取碼。 方案選擇 單片機的選…

SpringBoot 讀取配置文件

Spring Boot 中讀取配置文件有以下 5 種方法&#xff1a; 使用 Value 讀取配置文件。使用 ConfigurationProperties 讀取配置文件。使用 Environment 讀取配置文件。 Autowired private Environment environment; 實現EnvironmentAware接口 使用 PropertySource 讀取配置文件…

Python學習筆記_進階篇(一)_淺析tornado web框架

tornado簡介 1、tornado概述 Tornado就是我們在 FriendFeed 的 Web 服務器及其常用工具的開源版本。Tornado 和現在的主流 Web 服務器框架&#xff08;包括大多數 Python 的框架&#xff09;有著明顯的區別&#xff1a;它是非阻塞式服務器&#xff0c;而且速度相當快。得利于…

2023國賽數學建模思路 - 復盤:人力資源安排的最優化模型

文章目錄 0 賽題思路1 描述2 問題概括3 建模過程3.1 邊界說明3.2 符號約定3.3 分析3.4 模型建立3.5 模型求解 4 模型評價與推廣5 實現代碼 建模資料 0 賽題思路 &#xff08;賽題出來以后第一時間在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

衣服材質等整理(時常更新)

參考文章&圖片來源 https://zhuanlan.zhihu.com/p/390341736 00. 天然纖維 01. 化學纖維 02. 聚酯纖維&#xff08;即&#xff0c;滌綸&#xff09; 一種由有機二元酸和二元醇通過化學縮聚制成的合成纖維。具有出色的抗皺性和保形性&#xff0c;所制衣物在穿著過程中不容…

Lua + mysql 實戰代碼

--[[luarocks lua語言的包管理器luasql https://luarocks.org/brew install luarocksluarocks install luasql-mysql 注意此處&#xff0c;如果你是 mariadb&#xff0c;然后要求指定 MYSQL_DIR 參數的時候&#xff0c;千萬不要指到 mariadb 的安裝目錄&#xff0c;而是要指…

linux通過NC工具啟動臨時端口監聽

1.安裝nc工具 yum install nc -y2. 啟動監聽指定端口 #例如監聽8080端口 nc -lk 8080#后臺監聽 nc -lk 8080 &3. 驗證 #通過另外一臺網絡能通的機器&#xff0c;telnet 該機器ip 監聽端口能通&#xff0c;并且能接手數據 telnet 192.xxx.xxx.xx 8080

單機編排docker compose

Docker之旅(8)-單機編排docker compose 當在宿主機啟動較多的容器時候&#xff0c;如果都是手動操作會覺得比較麻煩而且容易出錯&#xff0c; 并且每個容器之間也會有先后啟動的順序依賴等。這個時候推薦使用 docker 單機 編排工具 docker-compose&#xff0c;docker-compose …

爬蟲逆向實戰(十四)--某培訓平臺登錄

一、數據接口分析 主頁地址&#xff1a;某培訓平臺 1、抓包 通過抓包可以發現登錄是表單提交到j_spring_security_check 2、判斷是否有加密參數 請求參數是否加密&#xff1f; 通過查看“載荷”模塊可以發現有一個j_password加密參數 請求頭是否加密&#xff1f; 無響應是…

2024浙大MBA/MEM/MPA四個月沖刺備考策略

近期收到很多考生的咨詢&#xff1a;距離聯考就僅剩四個多月的時間&#xff0c;這個管理類聯考的難度如何&#xff1f;主要考些什么內容&#xff1f;現在才開始備考還有希望上岸浙大嗎&#xff1f;是不是要等到明年在開始備考比較合適&#xff1f;那么今天在這里小立老師就跟大…

Docker Dockerfile 使用方法

目錄 Dockerfile 介紹 創建Dockerfile文件 構建 Docker 鏡像 查看已下載的鏡像 運行 mysql 命令 Dockerfile 介紹 當使用Docker構建容器化應用程序時&#xff0c;Dockerfile是一個用于定義容器鏡像的文本文件。它包含了一系列指令&#xff0c;告訴Docker如何從基礎鏡像&a…

? 將本地已有的項目上傳到 git 倉庫

目錄 ? 將本地已有的項目上傳到 git 倉庫&#x1f3ed; 一、克隆 拷貝&#x1f3a8; 二、強行合并兩個倉庫 ? 將本地已有的項目上傳到 git 倉庫 有兩種方法&#xff1a; ? 一、克隆 拷貝 ? 二、強行合并兩個倉庫 &#x1f3ed; 一、克隆 拷貝 ? 直接用把遠程倉庫拉到本…