1.篩法
int prime[maxn];
bool is_prime[maxn];
int sieve(int n){
int p = 0;
for(int i = 0; i <= n; ++i)
is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n; ++i){ // 注意數組大小是n
if(is_prime[i]){
prime[p++] = i;
for(int j = i + i; j <= n; j += i) // 輕剪枝,j必定是i的倍數
is_prime[j] = false;
}
}
return p; // 返回素數個數
}
2.快速冪算法
typedef long long LL; // 視數據大小的情況而定
LL powerMod(LL x, LL n, LL m)
{
LL res = 1;
while (n > 0){
if (n & 1) // 判斷是否爲奇數,若是則true
res = (res * x) % m;
x = (x * x) % m;
n >>= 1; // 相當於n /= 2;
}
return res;
}
3.大數階乘
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int num[maxn], len;
/*
在mult函數中,形參部分:len每次調用函數都會發生改變,n表示每次要乘以的數,最終返回的是結果的長度
tip: 階乘都是先求之前的(n-1)!來求n!
初始化Init函數很重要,不要落下
*/
void Init() {
len = 1;
num[0] = 1;
}
int mult(int num[], int len, int n) {
LL tmp = 0;
for(LL i = 0; i < len; ++i) {
tmp = tmp + num[i] * n; //從最低位開始,等號左邊的tmp表示當前位,右邊的tmp表示進位(之前進的位)
num[i] = tmp % 10; // 保存在對應的數組位置,即去掉進位後的一位數
tmp = tmp / 10; // 取整用於再次循環,與n和下一個位置的乘積相加
}
while(tmp) { // 之後的進位處理
num[len++] = tmp % 10;
tmp = tmp / 10;
}
return len;
}
int main() {
Init();
int n;
n = 1977; // 求的階乘數
for(int i = 2; i <= n; ++i) {
len = mult(num, len, i);
}
for(int i = len - 1; i >= 0; --i)
printf("%d",num[i]); // 從最高位依次輸出,數據比較多采用printf輸出
printf("\n");
return 0;
}
4.大數加法
#include<stdio.h>
#include<string.h>
char s1[1001],s2[1001];
int a[1001],b[1001],c[1001];
int main()
{
int i,k,n,m;
scanf("%s%s",s1,s2);
memset(c,0,sizeof(c));
n=strlen(s1);
m=strlen(s2);
for(i=0; i<n; i++)
a[i]=s1[n-i-1]-'0';
for(i=0; i<m; i++)
b[i]=s2[m-i-1]-'0';
if(n>m) k=n;
else k=m;
for(i=0; i<=k; i++)
{
c[i]+=a[i]+b[i];
if(c[i]>9)
{
c[i+1]++;
c[i]%=10;
}
}
i=k;
while(c[i]==0)
i--;
if(i<0) printf("0");
else
{
for(;i>=0;i--)
printf("%d",c[i]);
}
printf("\n");
return 0;
}
5.全排列
void Pern(int list[], int k, int n) { // k表示前k個數不動僅移動後面n-k位數
if (k == n - 1) {
for (int i = 0; i < n; i++) {
printf("%d", list[i]);
}
printf("\n");
}else {
for (int i = k; i < n; i++) { // 輸出的是滿足移動條件所有全排列
swap(list[k], list[i]);
Pern(list, k + 1, n);
swap(list[k], list[i]);
}
}
}
6.二分搜索
// left爲最開始元素, right是末尾元素的下一個數,x是要找的數
int bsearch(int *A, int left, int right, int x){
int m;
while (left < right){
m = left + (right - left) / 2;
if (A[m] >= x) right = m; else left = m + 1;
// 如果要替換爲 upper_bound, 改爲:if (A[m] <= v) x = m+1; else y = m;
}
return left;
}
/*
最後left == right
如果沒有找到135577找6,返回7
如果找有多少的x,可以用lower_bound查找一遍,upper_bound查找一遍,下標相減
C++自帶的lower_bound(a,a+n,x)返回數組中最後一個x的下一個數的地址
upper_bound(a,a+n,x)返回數組中第一個x的地址
如果a+n內沒有找到x或x的下一個地址,返回a+n的地址
lower_bound(a,a+n,x)-upper_bound(a,a+n,x)返回數組中x的個數
*/
7.二分法求最長遞增子序列
#include<bits/stdc++.h>
using namespace std;
int n;
int ch[100005];
int dp[100005];
int Binary(int x,int len1)
{
int left=1;
int right=len1;
while(left<=right)
{
int mid1=left+(right-left)/2;
if(dp[mid1]==x)
return mid1;
else if(dp[mid1]<x)
{
left=mid1+1;
}
else
right=mid1-1;
}
return left;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
{
cin>>ch[i];
}
memset(dp,0,sizeof(dp));
dp[1]=ch[1];
int len1=1;
for(int i=2;i<=n;i++)
{
if(ch[i]>dp[len1])
{
dp[++len1]=ch[i];
}
else
{
int t=Binary(ch[i],len1);
dp[t]=ch[i];
}
}
cout<<len1<<endl;
}
return 0;
}
8,。最長公共子序列
void getnext(char str[maxn], int nextt[maxn]) {
int j = 0, k = -1;
nextt[0] = -1;
while (j < m) {
if (k == -1 || str[j] == str[k]) {
j++;
k++;
nextt[j] = k;
}
else
k = nextt[k];
}
}
void kmp(int a[maxn], int b[maxn]) {
int nextt[maxm];
int i = 0, j = 0;
getnext(b, nextt);
while (i < n) {
if (j == -1 || a[i] == b[j]) { // 母串不動,子串移動
j++;
i++;
}
else {
// i不需要回溯了
// i = i - j + 1;
j = nextt[j];
}
if (j == m) {
printf("%d\n", i - m + 1); // 母串的位置減去子串的長度+1
return;
}
}
printf("-1\n");
}
9.並查集
int father[maxn]; // 儲存i的father父節點
void makeSet() {
for (int i = 0; i < maxn; i++)
father[i] = i;
}
int findRoot(int x) { // 迭代找根節點
int root = x; // 根節點
while (root != father[root]) { // 尋找根節點
root = father[root];
}
while (x != root) {
int tmp = father[x];
father[x] = root; // 根節點賦值
x = tmp;
}
return root;
}
void Union(int x, int y) { // 將x所在的集合和y所在的集合整合起來形成一個集合。
int a, b;
a = findRoot(x);
b = findRoot(y);
father[a] = b; // y連在x的根節點上 或father[b] = a爲x連在y的根節點上;
}
/*
在findRoot(x)中:
路徑壓縮 迭代 最優版
關鍵在於在路徑上的每個節點都可以直接連接到根上
*/
10.克魯斯卡爾算法
/*
第一步:點、邊、加入vector,把所有邊按從小到大排序
第二步:並查集部分 + 下面的code
*/
void Kruskal() {
ans = 0;
for (int i = 0; i<len; i++) {
if (Find(edge[i].a) != Find(edge[i].b)) {
Union(edge[i].a, edge[i].b);
ans += edge[i].len;
}
}
}
11.普利姆算法
struct node {
int v, len;
node(int v = 0, int len = 0) :v(v), len(len) {}
bool operator < (const node &a)const { // 加入隊列的元素自動按距離從小到大排序
return len> a.len;
}
};
vector<node> G[maxn];
int vis[maxn];
int dis[maxn];
void init() {
for (int i = 0; i<maxn; i++) {
G[i].clear();
dis[i] = INF;
vis[i] = false;
}
}
int Prim(int s) {
priority_queue<node>Q; // 定義優先隊列
int ans = 0;
Q.push(node(s,0)); // 起點加入隊列
while (!Q.empty()) {
node now = Q.top(); Q.pop(); // 取出距離最小的點
int v = now.v;
if (vis[v]) continue; // 同一個節點,可能會推入2次或2次以上隊列,這樣第一個被標記後,剩下的需要直接跳過。
vis[v] = true; // 標記一下
ans += now.len;
for (int i = 0; i<G[v].size(); i++) { // 開始更新
int v2 = G[v][i].v;
int len = G[v][i].len;
if (!vis[v2] && dis[v2] > len) {
dis[v2] = len;
Q.push(node(v2, dis[v2])); // 更新的點加入隊列並排序
}
}
}
return ans;
}
12.迪傑斯特拉算法
struct node {
int v, len;
node(int v = 0, int len = 0) :v(v), len(len) {}
bool operator < (const node &a)const { // 距離從小到大排序
return len > a.len;
}
};
vector<node>G[maxn];
bool vis[maxn];
int dis[maxn];
void init() {
for (int i = 0; i<maxn; i++) {
G[i].clear();
vis[i] = false;
dis[i] = INF;
}
}
int dijkstra(int s, int e) {
priority_queue<node>Q;
Q.push(node(s, 0)); // 加入隊列並排序
dis[s] = 0;
while (!Q.empty()) {
node now = Q.top(); // 取出當前最小的
Q.pop();
int v = now.v;
if (vis[v]) continue; // 如果標記過了, 直接continue
vis[v] = true;
for (int i = 0; i<G[v].size(); i++) { // 更新
int v2 = G[v][i].v;
int len = G[v][i].len;
if (!vis[v2] && dis[v2] > dis[v] + len) {
dis[v2] = dis[v] + len;
Q.push(node(v2, dis[v2]));
}
}
}
return dis[e];
}
13.弗洛伊德算法
for (int i = 0; i < n; i++) { // 初始化爲0
for (int j = 0; j < n; j++)
scanf("%lf", &dis[i][j]);
}
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
14.揹包
// 01揹包:
void bag01(int cost,int weight) {
for(i = v; i >= cost; --i)
dp[i] = max(dp[i], dp[i-cost]+weight);
}
// 完全揹包:
void complete(int cost, int weight) {
for(i = cost ; i <= v; ++i)
dp[i] = max(dp[i], dp[i - cost] + weight);
}
// 多重揹包:
void multiply(int cost, int weight, int amount) {
if(cost * amount >= v)
complete(cost, weight);
else{
k = 1;
while (k < amount){
bag01(k * cost, k * weight);
amount -= k;
k += k;
}
bag01(cost * amount, weight * amount);
}
}
15.博弈論
BASH
#define _MAX 10000
int a[_MAX];
int b[_MAX];
int bash(int N, int K)
{
if (N % (K + 1) == 0)
{
return 2;
}
return 1;
}
int main()
{
int T;
scanf("%d", &T);
for (int i = 0; i < T; i++)
{
scanf("%d%d", a + i, b + i);
}
for (int i = 0; i < T; i++)
{
if (bash(a[i], b[i]) == 1)
{
printf("A\n");
}
else
{
printf("B\n");
}
}
return 0;
}
NIM
int main(int argc, const char * argv[])
{
int N, stone, tag = 0;
scanf("%d", &N);
while (N--)
{
scanf("%d", &stone);
tag ^= stone;
}
//tag爲0則爲後手贏,否則爲先手贏
printf("%c\n", tag == 0 ? 'B' : 'A');
return 0;
}
SG函數打表
const int MAX_DIG = 64;
// SG打表
// f[]:可以取走的石子個數
// sg[]:0~n的SG函數值
// hash[]:mex{}
int f[MAX_DIG];
int sg[MAX_DIG];
int hash[MAX_DIG];
void getSG(int n)
{
memset(sg, 0, sizeof(sg));
for (int i = 1; i <= n; i++)
{
memset(hash, 0, sizeof(hash));
for (int j = 1; f[j] <= i; j++)
{
hash[sg[i - f[j]]] = 1;
}
for (int j = 0; j <= n; j++) // 求mes{}中未出現的最小的非負整數
{
if (hash[j] == 0)
{
sg[i] = j;
break;
}
}
}
}
SG DFS
const int MAX_DIG = 64;
// DFS
// 注意 S數組要按從小到大排序 SG函數要初始化爲-1 對於每個集合只需初始化1遍
// n是集合s的大小 S[i]是定義的特殊取法規則的數組
int s[MAX_DIG];
int sg[MAX_DIG * 100];
int n;
int SG_dfs(int x)
{
if (sg[x] != -1)
{
return sg[x];
}
bool vis[MAX_DIG];
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++)
{
if (x >= s[i])
{
SG_dfs(x - s[i]);
vis[sg[x - s[i]]] = 1;
}
}
int e;
for (int i = 0; ; i++)
{
if (!vis[i])
{
e = i;
break;
}
}
return sg[x] = e;
}
Wythoff
int main()
{
int t, a, b, m, k;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &a, &b);
if (a > b)
{
a ^= b;
b ^= a;
a ^= b;
}
m = b - a;
k = (int)(m * (1 + sqrt(5)) / 2.0);
//m = ? * a
//k = m / ?
//?:黃金分割數
//如果a == k,則爲後手贏,否則先手贏(奇異局)
printf("%s\n", a == k ? "B" : "A");
}
return 0;
}