ACM常用的一些技巧

     趁今天有点时间,整理下oj题型C/C++的一些标准库函数的使用!

1、快排

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[]={2,3,6,9,10,2,0};
	int i;
	int len=sizeof(a)/sizeof(int);
	sort(a,a+len) ;
	printf("%d\n",len);
	for(i=0;i<len;i++)
	printf("%d,",a[i]);
	return 0;
} 

2、去重(注意unique返回的是去重后的最后一个元素地址,必须先排序)

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[]={2,2,3,3,6,5,7};
	int len=sizeof(a)/sizeof(int);
	
	sort(a,a+len);
	int count=unique(a,a+len)-a;
	
	printf("不重复有%d个\n",count);
	for(int i=0;i<count;i++)
	printf("%d,",a[i]);
	return 0;
} 

3、最大公约数

int gcd(int a,int b){  return b?gcd(b,a%b):a;}

4、最下公倍数

lcm = a * b / gcd(a,b)

5、字符数组转换为整型(注意:只能是char[],如果用string的话,会出错的)

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	char a[]={'3','0','9'};
	int n;
	sscanf(a,"%d",&n);
	printf("%d\n",n);
	return 0;
} 

char s[100];

int x=atoi(s);

上面两行代码也可以将s转为数字。

6、求n!的位数

for(int i=2;i<=n;i++)

 Len+=log10(i*1.0); 

 ans=(int)len+1;

斯特林公式:这里求到的数和原本的实际值相差不大,故求位数不影响

len=0.5*log10(2*3.1415927*n)+n*log10(n/2.718281828459); 

ans=(int)len+1;

7、Log与自然对数e

Log(x)表示ln(x)

其他例如:log10(x) ,log2(x),

用exp(x)来表示e^x

8、素数(N以内有大概num=N/ln(x)个素数,N越大越准)

简单素数打表:O(n*sqrt(n))

void prim(){
	int num = 0;
	for (int i = 2; i < maxn; i++){
		int k = 1;
		for (int j = 2; j <= (int)sqrt(i);j++)
			if (i%j == 0) { k = 0; break; }
		if (k) pri[num++] = i;
	}
}

高效素数打表:O(n)

(线性筛选法——欧拉筛法)

/*
    |埃式筛法|
    |快速筛选素数|
*/
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int prime[100];  
bool is_prime[100];
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;   //  返回素数个数
}

int main()
{
	int len=sieve(50);
	for(int i=0;i<len;i++)
	{
		printf("%d,",prime[i]);
	}
	return 0;
}

9、分解质因数

首先理解什么叫做合数:除了能被1与本身整除外还能被其他非0数整除,最下合数是4。与之对应的是素数也就是质数。

每个合数都可以写成几个质数(素数)相乘的形式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数。如30=2×3×5 。分解质因数只针对合数。 

要用到容器,所以参考https://blog.csdn.net/u013719339/article/details/80615217

向量基本操作 

//初始化
vector<int> vec //默认初始化,空
vector<int> vec2(vec); //使用vec初始化vec2
vector<int> vec3(3); //初始化3个值为0的元素
vector<int> vec4(4, 1); //初始化4个值为1的元素
vector<string> vec5(5,"null"); //初始化5个值为null的元素
vector<string> vec6(6,"hello"); //初始化6个值为hello的元素
 
//常用的操作方法
vec.push_back(3) //向末尾添加元素3
vec.end(); //返回指向末尾下一位置的迭代器
int size = vec.size() //一共的元素个数
bool isEmpty = vec.empty(); //判断是否为空
cout<<vec[0]<<endl; //取得第一个元素
vec.insert(vec.end(),5,3); //从末尾下一位置插入5个值为3的元素
vec.pop_back(); //删除末尾元素
vec.erase(vec.begin(),vec.end());//删除之间的元素,其他元素前移
cout<<(vec==vec2)?true:false; //判断是否相等==、!=、>=、<=...
vector<int>::iterator iter = vec.begin(); //获取迭代器首地址
vector<int>::const_iterator c_iter = vec.begin(); //获取const类型迭代器
vec.clear(); //清空元素
 
//遍历方法
//下标法(vector的特有访问方法,一般容器只能通过迭代器访问)
int length = vec1.size();
for(int i=0;i<length;i++)
{
   cout<<vec1[i];
}
cout<<endl<<endl;
//迭代器法
vector<int>::const_iterator iterator = vec1.begin();
for(; iterator != vec1.end(); iterator++)
{
   cout<<*iterator;
}
 
//赋值与swap
vector<string> vs1(3); // vs1有3个元素
vector<string> vs(5); // vs2有5个元素
vs1.swap(vs2); //执行后,vs1中5个元素,而vs2则存3个元素

 列表基本操作:

//初始化
list<int> lst1; //创建空list
list<int> lst2(3); //创建含有三个元素的list
list<int> lst3(3,2); //创建含有三个元素的值为2的list
list<int> lst4(lst2); //使用lst2初始化lst4
 
//常用的操作方法
lst1.assign(lst2.begin(),lst2.end()); //分配值
lst1.push_back(10); //添加值
lst1.pop_back(); //删除末尾值
lst1.begin(); //返回首值的迭代器
lst1.end(); //返回末尾位置下一位的迭代器
lst1.clear();//清空值
bool isEmpty1 = lst1.empty(); //判断为空
lst1.erase(lst1.begin(),lst1.end()); //删除元素
lst1.front(); //返回第一个元素的引用
lst1.back(); //返回最后一个元素的引用
lst1.insert(lst1.begin(),3,2); //从指定位置插入3个值为2的元素
lst1.rbegin(); //返回第一个元素的前向指针
lst1.remove(2); //相同的元素全部删除
lst1.reverse(); //反转
lst1.size(); //含有元素个数
lst1.sort(); //排序
lst1.unique(); //删除相邻重复元素
 
//遍历方法
//迭代器法
for(list<int>::const_iterator iter = lst1.begin();iter != lst1.end();iter++)
{
   cout<<*iter;
}
cout<<endl;
 
//赋值与swap
list<string> sl1,sl2;
for(int i=0;i<10;i++)sl2.push_back("a");
sl1.assign(10, "A"); //s1被重新赋值,拥有十个元素,都为A

分解质因数

#include<iostream>
#include<algorithm>
#include<cstring>  	//memset() 初始化与字符串操作 
#include<cmath>		//数学库 
#include<vector>	//向量 
using namespace std;
vector<int> p; 

void fun(long long x) {//得到的是所有质因数 eg:24=》 2,2,2,3 
	p.clear();
	for (long long i = 2; i <=sqrt(x); i++){
		while (x%i == 0){
			p.push_back(i);
			x /= i;
		}	
	}    
       if (x != 1) p.push_back(x);   
 }

int main()
{
	fun(24);
	for(int i=0;i<p.size();i++)
	printf("%d,",p[i]);
	return 0;
}

10、吉姆拉尔森公式

           1.用途:给你年月日,计算今天星期几

           2.公式:Week = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1) mod 7;

           其中: d为几号,m为月份,y为年份

           3.注:把一月和二月看为是上一年的十三月和十四月!!

int CaculateWeekDay(int y,int m, int d)  
{  
    if(m==1||m==2){//一二月换算
        m+=12;
        y--;
    }
    int week = (d + 2*m +3*(m+1)/5 + y + y/4 - y/100 + y/400 + 1)%7;
    return week;//其中1~7表示周一到周日
} 

int main()
{
	printf("%d,",CaculateWeekDay(2019,4,20));
	return 0;
}

 

11、高精度计算n!;

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int factorial(int n)
	{
		long a[10000];
		int i,j,l,c,m=0,w;
		a[0]=1;
		for(i=1;i<=n;i++)
		{
			c=0;
			for(j=0;j<=m;j++)
			{
				a[j]=a[j]*i+c;
				c=a[j]/10000;
				a[j]=a[j]%10000;
			}
			if(c>0)
			{
				m++;
				a[m]=c;
			}
		}
		w=m*4+log10(a[m])+1;
		printf("%ld",a[m]) ;
		for(i=m-1;i>=0;i--)
		printf("%4.4ld",a[i]);
		return w;
	}
int main()
{
	printf("\n一共%d位!\n",factorial(10));
	return 0;
} 

12、高精度计算乘法

13、高精度计算加法

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
/*
    |大数模拟加法|
    |用string模拟|
*/

string add(string s1, string s2)
{
    if (s1 == "" && s2 == "")   return "0";
    if (s1 == "")   return s2;
    if (s2 == "")   return s1;
    string maxx = s1, minn = s2;
    if (s1.length() < s2.length()){
        maxx = s2;
        minn = s1;
    }
    int a = maxx.length() - 1, b = minn.length() - 1;
    for (int i = b; i >= 0; --i){
        maxx[a--] += minn[i] - '0'; //  a一直在减 , 额外还要减个'0'
    }
    for (int i = maxx.length()-1; i > 0;--i){
        if (maxx[i] > '9'){
            maxx[i] -= 10;//注意这个是减10
            maxx[i - 1]++;
        }
    }
    if (maxx[0] > '9'){
        maxx[0] -= 10;
        maxx = '1' + maxx;
    }
    return maxx;
}

int main()
{
	string result;
	result=add("11111111","22222222");
	printf("%s\n",result.c_str());
	printf("len=%d",strlen(result.c_str()));
	return 0;
}

14、高精度计算减法

15、快速幂(计算a^b mod m)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
/*
    |快速幂|
*/
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;
}

int main()
{
//	计算x^n mod m 
	int x=powerMod(3,2,5);
	printf("%lld",x);
	return 0;
}

16、全排列

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int list[]={1,2,3,4,5,6}; 
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]);
        }
    }
}

int main()
{
	Pern(list,3,6);
	return 0;
}

17、二分搜索


// 1.求最小的i,使得a[i] = key,若不存在,则返回-1
int ans = std::lower_bound(a, a + n, key) - a;
ans = (ans == n || a[ans] != key) ? -1 : ans;
 
// 2.求最大的i,使得a[i] = key,若不存在,则返回-1
int ans = std::upper_bound(a, a + n, key) - a;
ans = (ans == 0 || a[ans - 1] != key) ? -1 : ans - 1;
 
// 3.求最小的i,使得a[i] > key,若不存在,则返回-1
int ans = std::upper_bound(a, a + n, key) - a;
ans = (ans == n) ? -1 : ans;
 
// 4.求最大的i,使得a[i] < key,若不存在,则返回-1
int ans = std::lower_bound(a, a + n, key) - a;
ans = (ans == 0) ? -1 : ans - 1;

18、

=============图====论===============

MST

最小生成树

Kruskal(克鲁斯卡尔算法)

/*
    |Kruskal算法|
    |适用于 稀疏图 求最小生成树|
    |16/11/05ztx thanks to wangqiqi|
*/

/*
    第一步:点、边、加入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;    
        }    
    }    
}    

19、

Prim:普里姆算法

/*
    |Prim算法|
    |适用于 稠密图 求最小生成树|
    |堆优化版,时间复杂度:O(elgn)|
    |16/11/05ztx, thanks to chaixiaojun|
*/

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; 
}  

20、

Bellman-Ford

单源最短路

Dijkstra:迪杰斯特拉算法

/*
    |Dijkstra算法|
    |适用于边权为正的有向图或者无向图|
    |求从单个源点出发,到所有节点的最短路|
    |优化版:时间复杂度 O(elbn)|
    |16/11/05ztx, thanks to chaixiaojun|
*/

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];  
}  

21、

SPFA.最短路径快速算法(Shortest Path Faster Algorithm)

/*
    |SPFA算法|
    |队列优化|
    |可处理负环|
*/

vector<node> G[maxn];
bool inqueue[maxn];
int dist[maxn];

void Init()  
{  
    for(int i = 0 ; i < maxn ; ++i){  
        G[i].clear();  
        dist[i] = INF;  
    }  
}  
int SPFA(int s,int e)  
{  
    int v1,v2,weight;  
    queue<int> Q;  
    memset(inqueue,false,sizeof(inqueue)); // 标记是否在队列中  
    memset(cnt,0,sizeof(cnt)); // 加入队列的次数  
    dist[s] = 0;  
    Q.push(s); // 起点加入队列  
    inqueue[s] = true; // 标记  
    while(!Q.empty()){  
        v1 = Q.front();  
        Q.pop();  
        inqueue[v1] = false; // 取消标记  
        for(int i = 0 ; i < G[v1].size() ; ++i){ // 搜索v1的链表  
            v2 = G[v1][i].vex;  
            weight = G[v1][i].weight;  
            if(dist[v2] > dist[v1] + weight){ // 松弛操作  
                dist[v2] = dist[v1] + weight;  
                if(inqueue[v2] == false){  // 再次加入队列  
                    inqueue[v2] = true;  
                    //cnt[v2]++;  // 判负环  
                    //if(cnt[v2] > n) return -1;  
                    Q.push(v2);  
                } } }  
    }  
    return dist[e];  
}

/*
    不断的将s的邻接点加入队列,取出不断的进行松弛操作,直到队列为空  

    如果一个结点被加入队列超过n-1次,那么显然图中有负环  
*/

22、

Floyd-Warshall:弗洛伊德算法

/*
    |Floyd算法|
    |任意点对最短路算法|
    |求图中任意两点的最短距离的算法|
*/

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]);  
        }  
    }
}

23、===========二分图==============

24、染色法

/*
    |交叉染色法判断二分图|
    |16/11/05ztx|
*/

int bipartite(int s) {  
    int u, v;  
    queue<int>Q;  
    color[s] = 1;  
    Q.push(s);  
    while (!Q.empty()) {  
        u = Q.front();  
        Q.pop();  
        for (int i = 0; i < G[u].size(); i++) {  
            v = G[u][i];  
            if (color[v] == 0) {  
                color[v] = -color[u];  
                Q.push(v);  
            }  
            else if (color[v] == color[u])  
                return 0;  
        }  
    }  
    return 1;  
}  

25、匈牙利算法

/*
    |求解最大匹配问题|
    |递归实现|
    |16/11/05ztx|
*/

vector<int>G[maxn];  
bool inpath[maxn];  //  标记  
int match[maxn];    //  记录匹配对象  
void init()  
{  
    memset(match, -1, sizeof(match));  
    for (int i = 0; i < maxn; ++i) {  
        G[i].clear();  
    }  
}  
bool findpath(int k) {  
    for (int i = 0; i < G[k].size(); ++i) {  
        int v = G[k][i];  
        if (!inpath[v]) {  
            inpath[v] = true;  
            if (match[v] == -1 || findpath(match[v])) { // 递归  
                match[v] = k; // 即匹配对象是“k妹子”的  
                return true;  
            }  
        }  
    }  
    return false;  
}  

void hungary() {  
    int cnt = 0;  
    for (int i = 1; i <= m; i++) {  // m为需要匹配的“妹子”数  
        memset(inpath, false, sizeof(inpath)); // 每次都要初始化  
        if (findpath(i)) cnt++;  
    }  
    cout << cnt << endl;  
}  

26、求解最大匹配问题

/*
    |求解最大匹配问题|
    |dfs实现|
    |16/11/05ztx|
*/

int v1, v2;  
bool Map[501][501];  
bool visit[501];  
int link[501];  
int result;  

bool dfs(int x)  {  
    for (int y = 1; y <= v2; ++y)  {  
        if (Map[x][y] && !visit[y])  {  
            visit[y] = true;  
            if (link[y] == 0 || dfs(link[y]))  {  
                link[y] = x;  
                return true;  
            } } }  
    return false;  
}  


void Search()  {  
    for (int x = 1; x <= v1; x++)  {  
        memset(visit,false,sizeof(visit));  
        if (dfs(x))  
            result++;  
    }
}

27、

28、

29、

扫描二维码关注公众号,回复: 10015663 查看本文章

30、

声明:我是大自然的搬运工,请叫我农夫山泉。上文很多内容搬运以下两位博主,详情参考原创!

https://blog.csdn.net/qq_32265245/article/details/53046750

https://blog.csdn.net/weixin_41156591/article/details/81809496

发布了44 篇原创文章 · 获赞 72 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/cj151525/article/details/89407326
今日推荐