1. 程式人生 > 其它 >絕世好題(動態規劃+位運算)

絕世好題(動態規劃+位運算)

位運算與動態規劃的詭異結合,全新的狀態設計,實現 $O(n)$ 時間複雜度。

給定一個長度為 \(n\) 的數列 \(a_i\),求 \(a_i\) 的子序列 \(b_i\) 的最長長度 \(k\),滿足 \(b_i\ \&\ b_{i-1} \ne 0\),其中 \(2\leq i\leq k\)\(\&\) 表示位運算取與。\(1\le n\le 10^5,1\le a_i\le 10^9\)

\(O(n^2)\) 的暴力是顯然的,有轉移方程 \(dp_i=\max\limits_{j=1,a_i\& a_j\ne 0}^{i-1}\{dp_j+1\}\),可以獲得 80 到 90 分。然而,這個式子沒有單調佇列、斜率優化等常見的特點,我們只能考慮從位運算性質入手優化。

現在的 \(dp_i\) 意味著 \(i\) 結尾的最長子序列長度,這一狀態與位運算很難建立聯絡。為什麼叫絕世好題啊?我們可以轉換思路,設計 \(dp_i\) 表示最後一項二進位制形式下第 \(i\) 位為 \(1\) 的最長子序列長度。如果感到難以理解,請仔細反覆閱讀上句定義,抽離關鍵詞,務必明白其意義。

每當我們讀入一個數 \(a_i\) 後,我們列舉其二進位制位 \(j\),求出每一個為 \(1\) 的二進位制位對應的 dp 值的最大值(因為 \(1\& 1\),所以這時更新合法),\(1\) 後反過來更新這些 dp 值

這種狀態設計首次遇到確實難以想出,只能慢慢積累了。

下面是 AC 程式碼片段:

for(int i=1,x,k;i<=n;++i) 
{
	scanf("%d",&x),k=1;
	for(int j=0;j<=30;++j) if((1<<j)&x) k=max(k,dp[j]+1);
	for(int j=0;j<=30;++j) if((1<<j)&x) dp[j]=max(dp[j],k);
	ans=max(ans,k);
}

THE END