刷题笔记

acm笔记

排序

快速排序

  • 快速排序

    给定你一个长度为n的整数数列。
    
    请你使用快速排序对这个数列按照从小到大进行排序。
    
    void Qsort(int L ,int R)
    {
    	if(L>=R)
    		return; 
    	int mid = p[(L+R)>>1],i=L-1,j =R+1;
    	while(i<j)
    	{
    		while(p[++i]<mid);
    		while(p[--j]>mid);
    		if(i<j)
    			swap( p[i], p[j]);
    	}
    	Qsort(L,j);
    	Qsort(j+1,R);
    }
    
  • 第k个数

    给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列从小到大排序后的第k个数。
    
    int Findkth(int L,int  R,int k)
    {
        if(L>=R)
            return p[L];
        int mid = p[(L+R)>>1];
        int i= L - 1,j = R + 1;
        while(i<j)
        {
            while(p[++i]<mid);
            while(p[--j]>mid);
            if(i<j)
                swap(p[i],p[j]);
        }
        int s = j - L + 1;
        if(s>=k)
            return Findkth(L,j,k);
        else
            return Findkth(j+1,R,k-s);
    }
    

归并排序

  • 归并排序

    void Mg(int L ,int R,int Rend)
    {
    	int i=L ,j=R,Lend = R -1;
    	int k = L;
    	while(i<=Lend&&j<=Rend)
    	{
    		if(p[i]<=p[j])
    			tmp[k++] = p[i++];
    		else
    			tmp[k++] = p[j++];
    	}
    	while(i<=Lend)
    		tmp[k++] = p[i++];
    	while(j<=Rend)
    		tmp[k++] = p[j++];
    	for(int i=L;i<k;i++)
    		p[i] = tmp[i];
    }
    void Msort(int L,int R)
    {
    	if(L<R)
    	{
    		int mid = (L+R)>>1;
    		Msort(L,mid);
    		Msort(mid+1,R);
    		Mg(L,mid+1,R);
    	}
    }
    
    ll Mg(int L ,int R,int Rend)
    {
    	ll num = 0 ;
    	int i=L ,j=R,Lend = R -1;
    	int k = L;
    	while(i<=Lend&&j<=Rend)
    	{
    		if(p[i]<=p[j])
    			tmp[k++] = p[i++];
    		else
    		{
    			num += R - i ;
    			tmp[k++] = p[j++];
    		}
    	}
    	while(i<=Lend)
    		tmp[k++] = p[i++];
    	while(j<=Rend)
    		tmp[k++] = p[j++];
    	for(int i=L;i<k;i++)
    		p[i] = tmp[i];
    	return num;
    }
    ll Msort(int L,int R)
    {
    	if(L<R)
    	{
    		int mid = (L+R)>>1;
    		ll s1 = Msort(L,mid);
    		ll s2 = Msort(mid+1,R);
    		ll s3 = Mg(L,mid+1,R);
    		return s1+s2+s3;
    	}
    	return 0;
    }
    

快速幂

快速幂

  • 非递归

    ll qpow(ll a,ll p,ll k)
    {
    	ll ans = 1;
    	while(p)
    	{
    		if(p&1) ans = (ans * a)%k;
    		p>>=1;
    		a =(a*a)%k;
    	}
    	return ans%k;
    } 
    
  • 递归

    ll qpow(ll a,ll p,ll k)
    {
    	if(p==0)
    		return 1%k;
    	ll b = qpow(a,p>>1,k);
    	if(p&1)
    		return ((b*b)%k*a)%k;
    	else
    		return (b*b)%k;
    } 
    

前缀和 差分

int n,m; // n 表示数量 m表示询问的次数
cin>>n>>m;
for(int i=1;i<=n;i++)
    cin>>p[i];
for(int i=1;i<=n;i++)
    p[i] =  p[i-1] + p[i]; // p[i-1] 为1~i-1的和 
while(m--)
{
    int a,b;
    cin>>a>>b;
    cout<<p[b] - p[a-1]<<endl; // 输出 a~b的和
} 
int n,m,q;
cin>>n>>m>>q; //  n 表示行数 m表示列数  q表示询问的次数
for(int i=1;i<=n;i++)
    for(int j =1;j<=m;j++)
        cin>>p[i][j];
for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        p[i][j] =  p[i][j-1] + p[i-1][j] - p[i-1][j-1] + p[i][j];
while(q--)
{
    int x1,x2,y1,y2;
    cin>>x1>>y1>>x2>>y2;
    cout<<p[x2][y2] -  p[x1-1][y2] - p[x2][y1-1] + p[x1-1][y1-1] <<endl;
} 
#include<iostream>
#include<cstdio>
using namespace std; 
const int N = 100010 ;
int p[N] ;
// 在 l~r加一个数c
void insert(int l,int r ,int c)
{
	p[l]+=c;
	p[r+1] -= c; 
}
int main()
{
	int n,m,num,a,b,c;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		cin>>num;
		insert(i,i,num);
	}
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b>>c;
		insert(a,b,c);
	}
	for(int i=1;i<=n;i++)
	{
		// 从 差分变为 前缀和 
		p[i] += p[i-1];
		printf("%d ",p[i]);
	}
	return 0;
} 
#include<iostream>
#include<cstdio>
using namespace std;
const int  N= 1010; 
int p[N][N];
void insert(int x1,int y1 ,int x2 ,int y2,int c)
{
	p[x1][y1] +=c;
	p[x2+1][y1] -=c;
	p[x1][y2+1] -=c;
	p[x2+1][y2+1] +=c;
}
int main()
{
	int n,m,q,num; 
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{ 
			scanf("%d",&num);
			insert(i,j,i,j,num);
		}			
	int a,b,c,d,f;
	while(q--)
	{
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&f);
		insert(a,b,c,d,f);
	}
	// 变成前缀和 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			p[i][j] = p[i][j] + p[i-1][j] +p[i][j-1] -p[i-1][j-1];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			printf("%d ",p[i][j]);
		printf("\n");
	}
	return 0;
} 

二分

二分的条件问题具有

  1. 二段性

  2. 单调性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Enp1p0is-1616208468787)(C:\Users\james\AppData\Roaming\Typora\typora-user-images\image-20210319075850437.png)]

答案在黄色的右端点上

if M 是红色 
    L = M + 1
else
    R = M

具体代码

while(L<R)
{
    M = (L+R)/2;
    if M 是红色 
    	L = M + 1
	else
    	R = M
}
return L

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rx8lvyQX-1616208468792)(C:\Users\james\AppData\Roaming\Typora\typora-user-images\image-20210319080345929.png)]

答案在红色的左端点上

if M 是红色
    L = M
else 
    R = M - 1

具体代码

while(L<R)
{
    M = (L+R+1)/2;  // 注意这里要加 1 !!!
    // 因为 M = (L+R)/2 是向下取整 如果 L = R - 1 ,M = R - 1 ,L = M = R - 1 死循环
    // 所以要向上取整
    if M 是红色
    	L = M      
	else 
    	R = M - 1
}
return L

浮点数二分算法

double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) 
            r = mid;
        else 
            l = mid;
    }
    return l;
}
  • 数的范围

    对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
    
    如果数组中不存在该元素,则返回 -1 -1
    

    找到起始位置

    int FindL(int k )
    {
        int L = 0 , R = n-1 ;
        int mid;
        while(L<R)
        {
            mid = (L+R)>>1;
            if(p[mid]<k)
                L = mid + 1;
            else
                R = mid;
        }
        if(p[L]==k)
            return L;
        return -1;
    }
    

    找到终止位置

    int FindR(int k )
    {
        int L = 0 , R = n-1 ;
        int mid;
        while(L<R)
        {
            mid = (L+R+1)>>1;
            if(p[mid]<=k)
                L = mid;
            else
                R = mid-1;
        }
        if(p[L]==k)
            return L;
        return -1;
    }
    
  • 数的三次方根

    double L = -10000,R = 10000;
    while(R-L>=e)
    {
        mid=(L+R)/2;
        k = mid*mid*mid;
        if(k<n)
            L = mid;
        else if(k>n)
            R = mid;
    }
    printf("%.6lf",L);
    

区间的合并

给定 n 个区间 [l,r],要求合并所有有交集的区间
int s=p[0].l,e=p[0].r;
for(int i=1;i<n;i++)
{
    if(p[i].l<=e)
        e = max(e,p[i].r);
    else
    {
        ans ++;
        s = p[i].l;
        e = p[i].r;
    }
}
// 保存每一个区间
a[k].l=p[0].l;
a[k].r=p[0].r;
int i=0;
for(;i<n;i++)
{
    if(p[i].l<=a[k].r)
    {
        a[k].r = max(a[k].r,p[i].r);
    }
    else
    {
        k++; 
        a[k].l=p[i].l;
        a[k].r=p[i].r;
    }
}

组合

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案
for(int i=0;i<(1<<n);i++)
{
    for(int j=0;j<n;j++)
        if(i&(1<<j))
            printf("%d ",j+1);
    printf("\n");
}
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案
for(int i=0;i<(1<<n);i++)
{
    int j = i,num=0;
    while(j)
    {
        if(j&1)
            num++;
        j>>=1;
    }
    if(num!=k)
        continue;
    for(int j=0;j<n;j++)
        if(i&(1<<j))
            printf("%d ",j+1);
    printf("\n");
}

并查集

//初始化
for(int i=1;i<=n;i++)
    f[i] = i;
// 查找
int Find(int k)
{
    if(k!=f[k]) f[k] = Find(f[k]);
    return f[k];
}

动态规划(dp)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值