1. 程式人生 > 實用技巧 >0x01 基本演算法-位運算

0x01 基本演算法-位運算

A題:a^b

https://ac.nowcoder.com/acm/contest/996/A

題目描述
求 a 的 b 次方對 p 取模的值,其中 0 <= a,b,p <= 10^9

輸入描述:
三個用空格隔開的整數a,b和p。

輸出描述:
一個整數,表示a^b mod p的值。

例項
輸入: 2 3 9
輸出: 8

思路
這道題是要先算出a的b次冪再對其結果進行求模(取餘),因為b最大可為1e+9,按普通做法來做時間複雜度就太大了,顯然這樣過不了題,
能快速算a的b次冪,就能減小時間複雜度,快速冪就是一種不錯的方法。

什麼是快速冪
快速冪是一種簡化運算底數的n次冪的演算法,理論上其時間複雜度為 O(log₂N),而一般的樸素演算法則需要O(N)的時間複雜度。簡單來說快速冪其實就是抽取了指數中的2的n次冪,將其轉換為時間複雜度為O(1)的二進位制移位運算,所以相應地,時間複雜度降低為O(log₂N)。

程式碼原理
\(a^{13}\) 為例,
先把指數13化為二進位制就是1101,把二進位制數字1101直觀地表現為十進位制則是如下的等式:

\[13 = 1 * (2^3) + 1 * (2^2) + 0 * (2^ 1) + 1 * (2^0) \]

這樣一來 \(a^{13}\) 可以如下算出:

\[a^{13} = a ^ {(2^3)} * a ^ {(2^2)} * a ^ {(2^0)} \]

完整AC程式碼如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;//將long long型別取個別名:ll型別,為了方便

int power(int a, int b, int mod) {
	ll ans = 1 % mod;
	for (; b; b >>= 1) {
		if (b & 1) ans = ans * a % mod;
		a = (ll)a * a % mod;//顯式轉化為ll型別進行高精度計算,再隱式轉化為int
	}
	return ans;
}

int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	int a, b, mod;
	cin >> a >> b >> mod;
	cout << power(a, b, mod) << endl;
}

C題:64位整數乘法

連結:https://ac.nowcoder.com/acm/contest/996/C

思路:

類似快速冪的思想,把整數b用二進位制表示,即

\[b = c_{k - 1}2^{k - 1} + c_{k -2}2^{k - 2} + ... + c_02^0 \]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	ll a, b, p; cin >> a >> b >> p;
	ll ans = 0;
	for (; b; b >>= 1) {
		if (b & 1)ans = (ans + a) % p;
		a = (a << 1) % p;
	}
	cout << ans << endl;
}

D題:最短Hamilton路徑

連結:https://ac.nowcoder.com/acm/contest/996/D

解題思路

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof a)
int e[21][21], b[1 << 21][21], n;
int main() {
	//freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j)
			cin >> e[i][j];
	ms(b, 0x3f); b[1][0] = 0;
	for (int i = 0; i < 1 << n; ++i)
		for (int j = 0; j < n; ++j) if (i >> j & 1)
			for (int k = 0; k < n; ++k) if (~(i >> k) & 1)//if ((i ^ 1 << j) >> k & 1)
				b[i + (1 << k)][k] = min(b[i + (1 << k)][k], b[i][j] + e[j][k]);
	cout << b[(1 << n) - 1][n - 1] << endl;
}