一些基本算法

	一般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()....就行了
posted @ 2020-04-26 12:11  arbor_one  阅读(133)  评论(0)    收藏  举报