一些基本算法

一般ACM或者笔试题的时间限制是1秒或2秒。
在这种情况下,C++代码中的操作次数控制在 107107 为最佳。
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
n≤30, 指数级别, dfs+剪枝,状态压缩dp
n≤100 => O(n3),floyd,dp
n≤1000 => O(n2),O(n2logn),dp,二分
n≤10000 => O(n∗n),块状链表
n≤100000 => O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、dijkstra+heap、spfa、求凸包、求半平面交、二分
n≤1000000 => O(n), 以及常数较小的 O(nlogn)O(nlogn) 算法 => hash、双指针扫描、kmp、AC自动机,常数比较小的 O(nlogn)O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n≤10000000 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
n≤109 => O(n√)O(n),判断质数
n≤1018 => O(logn),最大公约数
计算那一天是星期几 蔡勒公式
星期几 =n-1+(n-1)/4-(n-1)/100+(n-1)/400+c;
n是年份 c是这一年的第几天
一个数相同的质因子数有多少个
int get_primes(int n)
{
for(int i=2;i<=n/i;i++)
{
if(n%i==0){
int k=0;
while(n%i==0)
{
k++;
n/=i;
}
cout<<i<<" "<<k<<endl;
}
}
if(n>1)cout<<n<<" "<<1<<endl;
puts("");
}
最小公倍数
int a,b,c;
cin>>m>>n;
a=m;
b=n;
while(b!=0)
{
c=a%b;
a=b;
b=c;
}
printf("最大公约数是%d",a);
printf("最小公倍数是%d",m*n/a);
################################
最大公约数 int gcd(int a,int b)
{
return !b?a:gcd(b,a%b);
}
}

复数运算
a+bi c+di
加法
(a+c)+(b+d)i
减法
(a-c)+(b-d)i
乘法
(ac-bd)+(bc+ad)i
除法
(ac+bd)/(c^2+d^2)+(bc-ad)/(c^2+d^2)i
快速幂模板O(logk)
int qmi(int m,int k,int p)
{
int res=1%p,t=m;
while(k)
{
if(k&1)res=res*t%p;
t=t*t%p;
k>>=1;
}
return res;
}
二分模板O(logn)
二分模板一共有两个,分别适用于不同的情况;
算法思路:假设目标值在闭区间[l,r]中,每次将区间长度缩小一半,当l=r是,我们就找到了目标值。
版本一
当我们将区间[l,r]划分成[l,mid]和[mid+1,r]时,其更新操作是r=mid后者l=mid+1;计算mid时不需要加1;
int bsearch(int l,int r)
{
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
版本二
当我们将区间[l,r]划分成[l,mid-1]和[mid,r]时,其更新操作是,r=mid-1和l=mid,为了避免死循环,计算mid是要加1;
int bsearch(int l,int r)
{ int mid=l+r>>1;
while(l<r)
{
if(check(mid))l=mid;
else r=mid-1;
}
return l;
}
快排模板O(nlogn)
void quick_sort(int q[],int l,int r)
{
if(l>=r)return ;
int i=l-1,j=r+1,x=q[l+r>>1];
while(i<j)
{
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j)swap(q[i],q[j]);
}
quick_sort(q,l,j),quick(q,j+1,r);
}
归并排序()
void merge_sort(int q[],int l,int r)
{
if(l>=r)return;
int mid=l+r>>1;
merge_sort(q,l,mid);
merge_sort(q,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
if(q[i]<q[j])tmp[k++]=q[i++];
else tmp[k++]=q[j++];
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j++];
for(i=l,j=0;i<=r;i++,j++)q[i]=tmp[j];
}
线性筛法求素数
const int N = 1e7 + 9;
bool isprime[N] = { 0 };
int prime[N] = { 0 }, total;
void getprime(int size)
{
memset(isprime, 0, sizeof(isprime));
isprime[1] = false;
for (int i = 2; i < size; i++)
{
if (isprime[i] == 1)
prime[++total] = i;
for (int j = i; j <= total && i * prime[j] < size; j++)
{
isprime[i * prime[j]] =0;
if (i % prime[j] == 0)
{
break;
}
}
}
}
归并算法求逆序对
typdef long long ll;
ll cnt=0;//记录时逆序对个数
ll a[maxn];//原数组
ll b[maxn];//合并时的暂时数组
void merge(ll a[],ll l,ll r)
{
if(r-1<l)return;//当递归到区间长度只有1时,返回
ll mid=(l+r)/2;
merge(a,l,mid);
merge(a,mid+1,r);//分割成两个区间进行排序
ll i=l,j=mid+1;//i,j分别指向两个区间的开始
for(ll k=1;k<=r;k++)
{
if(j>r||(i<=mid&&a[i]<=a[j]))//当左区间遍历完成或在左区间没 遍历完的情况下左区间指向的元素不大于右区间指向的元素
b[k]=a[i++];//另临时数组获得左区间指向的元素,并指向下一个元素
else
{ //此时左区间元素小于右区间元素,则作曲兼的所有剩余元素都与右区间的当前元素构成逆序对
cnt+=mid-i+1;
b[k]=a[j++];
}
}
for(ll k=l;k<=r;k++)
a[k]=b[k];//合并的数组赋值给原数组
}
i2s
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::stringstream stream;
std::string result;
int i = 1000;
stream << i; //将int输入流
stream >> result; //从stream中抽取前面插入的int值
std::cout << result << std::endl; // print the string "1000"
}
#include<iostream>
#include<sstream>
using namespace std;
void i2s(int num,string &str)
{
stringstream ss;
ss<<num;
ss>>str;
}
int main()
{
int ans=0;
for(int i=10000;i<=99999;i++)
{
string s;
i2s(i,s);
if(s.find('4')==string::npos)
ans++;
}
cout<<ans<<endl;
}
kmp算法 O(n+m) //判断模式串(pattern)是不是文本串(text)的字串
//getnext得到长度为len的模式串(判断是不是字串的内个串)的next数组
void getnext(char s[],int len)//s是判断是不是字串的内个pattern
{
int j=-1;
next[0]=-1;//初始化 j=next[0]=-1;
for(int i=1;i<len;i++)
{
while(j!=-1&&s[i]!=s[j+1]) j=next[j];
//反复令j=next[j];
//直到j回退到-1,或者s[i]=s[j+1]
if(s[i]==s[j+1]) j++;
//如果s[i]==s[j+1],则next[i]=j+1,先令j指向这个位置。
next[i]=j; //令next[i]=j;
}
}
//至于下面的模板求模式串pattern匹配成功 、还是模式串pattern出现了多少次 、以及所有出现的位置的起始下标都差不多
void kmp(string text, string pattern)//判断是不是 就把前面改成bool
{
int n = text.length();
int m = pattern.length();//字符串长度
getNext(pattern,m);//计算pattern的next数组
int j = -1;
//如果是要输出匹配成功的次数 要加上ans=0;
for (int i = 0; i <n; i++)//试图匹配next[i];
{
while (j != -1 && text[i] != pattern[j + 1])
j = Next[j];//不断回退,直到j回到-1或者text[i]==pattern[j+1];
if (text[i] == pattern[j + 1])
j++;//text[i]与pattern[j+1]匹配成功,令j+1
if (j == m - 1)
{ ans++;//匹配成功的次数才加
ans.push_back(i-m+1);//匹配成功的下标才加
j = Next[j];//看是不是要让他继续匹配
return true;//这是判断是不是字串才加
}
}
return false;这是bool的时候才加
return ans;返回成功匹配次数
}
判断str[i...j]是否是回文串
bool isPalindrome(const char *str, int begin, int end)
{
while (begin <= end)
{
if (str[begin] == str[end])
{
begin++;
end--;
}
else
return false;
}
return true;
}
返回字符串str的最长回文子串的长度
int longestPalindrome(const char *str)
{
if (str == NULL)
return 0;
int len = strlen(str);
if (len == 1)
return 1;
int longest = 1;
for (int i = 0; i < len; i++)
for (int j = i + 1; j < len; j++)
if (isPalindrome(str, i, j) == true)
longest = max(longset,j-i+1);
return longest;
}
输出二进制
导入 #include<bitset>
int x; //x是四字节的
输出 cout<<bitset<sizeof(x)/4*5>(x)<<endl
输出的就是 x的五位二进制数
高精度乘法
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++ )
{
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
return C;
}
高精度减法
#include <iostream>
#include <vector>
using namespace std;
const int N = 1000010;
bool cmp(vector<int> &A, vector<int> &B)
{
if (A.size() != B.size()) return A.size() >= B.size();
for (int i = A.size() - 1; i >= 0; i--)
if (A[i] != B[i])
return A[i] > B[i];
return true;
}
void trimZero(vector<int> &A)
{
while (A.back() == 0 && A.size() > 1) A.pop_back();
}
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i++)
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
trimZero(C);
return C;
}
int main()
{
string a, b;
cin >> a >> b;
vector<int> A, B, C;
for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
trimZero(A), trimZero(B);
if (cmp(A, B)) C = sub(A, B);
else {
C = sub(B, A);
printf("-");
}
for (int i = C.size() - 1; i >= 0; i--) cout << C[i];
return 0;
}
高精度加法
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i ++ )
{
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
return C;
}
快速幂和高精度乘法结合
const int N=1000;
int ans[N],a[N];
ans[0]=ans[1]=a[0]=1;
a[1]=2;
void mul(int x[],int y[])
{
int c[N]={0};
c[0]=x[0]+y[0];
for(int i=0;i<x[0],i++)
for(int j=0;j<y[0];j++)
{ c[i+j+1]+=x[i+1]*y[i+1];
if(c[i+j+1]>=10)
{
c[i+j+2]+=c[i+j+1]/10;
c[i+j+1]%=10;
}
}
for(int i=0;i<=c[0];i++)
x[i]=c[i];
}
int p;
cin>p;
while(p!=0)
{
if(p&1)mul(ans,a);
else
mul(a,a);
}
最后输出的答案是 ans数组 倒序输出
矩阵乘法
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
for(int z=0;z<s;z++){
C[i][j] += A[i][z]*B[z][j];
}
邮票 dp存储的是在输入的数字中 需要最少几个数字可以得到下标的值
#include<iostream>
#include<algorithm>
using namespace std;
int dp[255010];//记录x的游资需要最少的邮票个数
int da[100];//data 邮票的邮资
int main()
{
int n, m;
int i, j;
cin >> n >> m;
for (i = 0; i < m; i++)
cin >> da[i];
int order = 0;//记录当前的序列
int found = 0;//当前的总有子是够否符合的邮票邮资
int minn;//当前最小使用的邮票数
int num;//差值 需要的邮资-邮票的邮资
while (1)
{
found = 0;
order++;
minn = 1000000000;
for (i = 0; i < m; i++)
{
num = order - da[i];//找到差值
if (num >= 0 && dp[num] + 1 < minn)//差值大于零并且选区的这个邮资的结果使得达到order的邮票个数最少
{
dp[order] = dp[num] + 1;//记录当前总邮资order的需要邮票数的最优值
minn = dp[num] + 1;//修改当前总邮资order的需要的邮票数的最优值
found = 1;
}
}
if (dp[order] > n || found == 0)
{
printf("%d", order - 1);
break;
}
}
return 0;
}
并查集
bool book[maxn];//存储的是每个树的最早的root
int pre[maxn];//存放的是每个数据的祖先
void init()
{
memset(book,0,sizeof(book));
for(inti=1;i<n*m;i++)
pre[i]=i;//先每个节点自己是自己的祖先
}
int find(int x)//找到自己的祖先
{
if(x!=pre[x])
return pre[x]=find(pre[x]);
return x;
}
void join(int x,int y)
{
int xx=find(x);//找到x节点的祖先
int yy=find(y);//找到y节点的祖先
if(xx!=yy)//如果他们不是同一个祖先
pre[yy]=xx;//我们就让他成为一个祖先
}
int main()....就行了
浙公网安备 33010602011771号