牛客网-数据结构笔试题目(四)-Powerful Ksenia问题解决方案(附源码)
題意
現在我們想要在n步這樣的神奇異或操作之內讓數組當中的所有元素全部相等,請問這一點是否可能呢?首先輸出YES或NO,表示是否有解。如果有解輸出需要操作的步數,以及對應選擇的元素下標。
樣例
在第一個樣例當中,4、1、7的異或結果為2,所以通過這樣一步操作之后,即可以滿足所有元素全部均等。
題解
我一開始的時候慣性思維,既然是異或運算,那么肯定要從二進制下手。一個數組當中的所有元素均等,其實就等價于它們在每一個二進制位上也等相等,同為0或者是同為1。于是我就一直在思考怎么來針對每一個二進制位來進行判斷和選擇,不知不覺就走進了死胡同,因為這些二進制位之間彼此影響, 我們很難一位一位地梳理清楚。
我之所以走進死胡同是因為被題目當中的一個條件給欺騙了,這個條件就是最多n個操作步驟的限制。我們直觀上都會覺得這是一個非常嚴苛的要求,所以會期望想到一個完美的解法,可以用最少的步驟解開這個問題。
但實際上這個n足夠大,足夠一些看起來非常笨的方法也能AC。不得不說這也是很多題目當中慣用的思維陷阱,考驗的就是選手的膽量和經驗。
異或的性質
首先我們來分析一下異或運算,這題當中并沒有對異或做什么特殊的處理。唯一不同的地方就是,我們是對三個數進行異或。我們從最基礎的01二進制位來分析,3個數做異或只有四種情況。000、010、011和111,我們發現其中000和011的結果都是0,010和111的結果是1。因為異或相同為0,不同為1的計算特性,會導致相同的數被消除。
比如我們計算的三個數是[a,b,b]那么最后的結果是a,我們可以利用這一點來做文章。想起來或許有些復雜,但是說穿了真的一文不值。
我們假設n=7,這7個數分別是[a,b,c,d,e,f,g]。首先我們對前三個數進行異或操作,這樣我們會得到:[h,h,h,d,e,f,g],接著我們選擇第3、4、5位的元素操作,得到:[h,h,j,j,j,f,g]。我們繼續選擇第5、6、7位的元素進行操作,得到[h,h,j,j,k,k,k]。
到這里其實已經有點眉目了,因為[a,b,b]的操作結果是a,我們剩下要做的就是繼續選擇,把除了k之外其他的元素全部消除。
我們繼續選擇第3、4、5位的元素操作,得到[h,h,k,k,k,k,k],同理我們最后選擇第1、2、3位的元素操作,這樣整個數組當中的元素都變成了k。到這里,我們一共進行了5次,也就是n-2次操作,完全沒有超過題目的限制。
但是這里有一個小問題,這個方案之所以可行是有前提的。它最大的前提就是n是奇數。如果n是偶數,就會最后剩下一個元素,這個應該怎么解決呢?
偶數的情況
偶數的情況我們光想是很難想出辦法來的,因為我們解決不了最后多余一個元素的問題。
這里需要用到一個關鍵性的推論,這個推論非常隱蔽,真的不容易想到。我們假設
,當n為偶數時,那么無論我們對這n個元素如何操作,這個異或得到的k保持不變。
這個結論是從哪里來的?其實也是從異或的性質當中來的。我們對三個數做異或,從具體某一個二進制位來分析。我們會發現我們的操作不會改變整體這n個bit的奇偶性。對于異或操作而言,兩兩相消,最后的結果只和奇偶性相關。最終的結果只和這個奇偶性相關。
從這一點出發,我們進一步可以得到如果
,那么一定無解。
這個結論其實也很簡單,因為我們已經知道了,無論我們如何操作也不會改變這個k值。由于n是偶數,所以如果n個數完全相等的話,那么它們的異或值一定等于0,所以k不等于0的時候,一定無解。
當k等于0的時候怎么辦呢?其實非常簡單,我們只需要拋棄掉最后一個元素,把之前的n-1個元素按照上面n為奇數時的操作全部操作相等即可。這樣一番操作之后,數組會變成這樣[a,a,a,a…a,b]。前面n-1個a的異或值為a,而整體n個數的異或值為0,所以可以得到a=b。那么我們就完成了整個操作。
整個思路有了之后,代碼實現就太簡單了。
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <cmath> #include <cstdlib> #include <string> #include <map> #include <set> #include <algorithm> #include <functional> #define rep(i,a,b) for (int i=a;i<b;i++) #define Rep(i,a,b) for (int i=a;i>=b;i--) #define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++) #define mid ((l+r)>>1) #define lson (k<<1) #define rson (k<<1|1) #define MEM(a,x) memset(a,x,sizeof a) #define L ch[r][0] #define R ch[r][1] const int N=100050; const long long Mod=1000000007;using namespace std;int a[N]; int main() {int n, x;scanf("%d", &n);rep(i, 0, n) {scanf("%d", a + i);}// 如果n為奇數,一定有解if (n % 2) {puts("YES");printf("%d\n", n-2);for (int i = 0; i + 2 < n; i+=2) {printf("%d %d %d\n", i+1, i+2, i+3);}for (int i = n-3; i - 2 >= 0; i-=2) {printf("%d %d %d\n", i+1, i, i-1);}}else {// 如果n為偶數,判斷整個數組的異或值是否為0x = 0;rep(i, 0, n) x ^= a[i];if (x > 0) {puts("NO");}else {n --;puts("YES");printf("%d\n", n-2);for (int i = 0; i + 2 < n; i+=2) {printf("%d %d %d\n", i+1, i+2, i+3);}for (int i = n-3; i - 2 >= 0; i-=2) {printf("%d %d %d\n", i+1, i, i-1);}}}return 0; }到這里,今天這道題就做完了,怎么樣,今天的題目還挺有意思吧。講道理把算法講出來之后非常簡單,幾乎沒有難度,但是如果讓我們自己思考,會變得非常難,我們很難從當中整理出思緒來。思路巧妙有趣這也是codeforces題目的最大魅力所在,希望大家都能體會到算法的樂趣。
總結
以上是生活随笔為你收集整理的牛客网-数据结构笔试题目(四)-Powerful Ksenia问题解决方案(附源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客网-数据结构笔试题目(三)-博弈论圆
- 下一篇: 谈谈实习期间应该注意的几点问题,助你早日