2018清北学堂集训提高组基础班D1

本文深入探讨了基础算法的多个实例,包括枚举算法的应用、经典问题的解决策略,如百鸡问题、最大子矩阵求解,以及算法优化技巧。通过具体代码示例,详细讲解了算法的设计与实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础算法

枚举

例一:判断一到n中有多少是n的倍数

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main(){
	int n;
	cin>>n;
	int tot=0;
	for(int i=1;i<=n;i++){
		if(i%3==0){
			tot++;
		}
	}
	cout<<tot<<endl;
	return 0;
} 

例一expexp

l-r中有几个是三的倍数

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int l,r;
	cin>>l>>r;
	cout<<r/3-(l-1)/3<<endl;
	return 0;
}

例一expexpexp

l-r中有多少是3或5或7的倍数

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int l,r;
	cin>>l>>r;
	int lans,rans;
	lans=(l-1)/3+(l-1)/5+(l-1)/7-(l-1)/15-(l-1)/21-(l-1)/35+(l-1)/105;
	rans=r/3+r/5+r/7-r/15-r/21-r/35+r/105;
	cout<<rans-lans<<endl;
	return 0;
 } 

例二

在一个笼子里有若干只鸡和若干只兔子,其中脚有x只,头有y个,问鸡和兔子各有几只

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;

int main(){
	int x,y;
	cin>>x>>y;
	int a,b;
	for(int i=0;i<=y;i++)
		{
			a=i;
			b=y-i;
			if(a*2+b*4==x){
				cout<<a<<" "<<b<<endl;
			}
		}
	return 0;
} 

例二exp

在一个笼子里有若干只鸡和若干只兔子,其中脚有x只,头有y个,问鸡和兔子各有几只。其中还存在若干只鸡单脚站立,问可能的方案总数

#include<iostream>
#include<cstdio>
using namespace std;

int x,y;
int a,b,c;
int ans=0;

int main(){
	cin>>x>>y;
	for(a=0;a<=y;a++)
		for(b=0;b<=y;b++){
			c=y-a-b;
			if(a*2+b*4+c==x){
				ans++;
			}
		}
	cout<<ans<<endl;
	return 0;
}

例三

你有一百块钱,一只公鸡5块钱,一只母鸡3块钱,三只小鸡一块钱,
百钱买百鸡,输出所有可能

#include<iostream>
#include<cstdio>
using namespace std;

int main(){
	int ans=0;
	int a,b,c;
	for(a=0;a<=100;a++){
		for(b=0;b<=100;b++){
			c=100-a-b;
			if(c>=0&&a*5+b*3+(c+2)/3==100&&a+b+c==100){
				cout<<a<<" "<<b<<" "<<c<<endl;
				ans++;
			}
		}
	}
	return 0;
} 

例四

质数判断

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

int main(){
	int n;
	cin>>n;
	bool a=1;
	for(int i=2;i<=int(sqrt(n));i++){
		if(n%i==0) a=false;
	}
	if(a){
		cout<<"yes"<<endl;
	}
	else
	cout<<"no"<<endl;
	return 0;
} 

洛谷 P2038 无线网络发射器选址

#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<cstring>
#include<algorithm>
using namespace std;

int main(){
	int d,n;
	cin>>d>>n;
	int x,y,k;
	int a[150][150]={0};
	for(int i=1;i<=n;i++){
		cin>>x>>y>>k;
		a[x][y]=k;
	}
	int x1,x2,y1,y2;
	int l,r;
	int tot=0,ans=0,cnt=0;
	for(int i=0;i<=128;i++){
		for(int j=0;j<=128;j++){
			if(i-d<0)
			x1=0;
			else
			x1=i-d;
			if(j-d<0)
			y1=0;
			else
			y1=j-d;
			if(i+d>128)
			x2=128;
			else
			x2=i+d;
			if(j+d>128)
			y2=128;
			else
			y2=j+d;
			for(int l=x1;l<=x2;l++){
				for(int r=y1;r<=y2;r++){
					tot=tot+a[l][r];
				}
			}
			if(tot>ans)
			{
				ans=tot;
				cnt=1;
			}
			else if(tot==ans){
				cnt++;
			}
			tot=0;
		}
	}
	cout<<cnt<<" "<<ans<<endl;
	return 0;
}

火柴棒等式

给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A、B、C是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字0-9的拼法如图所示:

注意:

  1. 加号与等号各自需要两根火柴棍
  2. 如果A≠B,则A+B=C与B+A=C视为不同的等式(A、B、C>=0)
  3. n根火柴棍必须全部用上
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int f[100001];
	f[0]=6;
	f[1]=2;
	f[2]=5;
	f[3]=5;
	f[4]=4;
	f[5]=5;
	f[6]=6;
	f[7]=3;
	f[8]=7;
	f[9]=6;
	int n;
	cin>>n;
	for(int i=10;i<=19999;i++)
	{
		f[i]=f[i/10]+f[i%10];
	}
	int sum=0;
	for(int a=0;a<=9999;a++)
		for(int b=0;b<=9999;b++)
		{
			int c=a+b;
			if(f[a]+2+f[b]+2+f[c]==n) sum++;
		}
	cout<<sum<<endl;
	return 0;
}

十进制分解

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int n;
	cin>>n;
	int cnt[10000];
	int i=0;
	while(n>0)
	{
		cnt[i++]=n%10;
		n=n/10;
	}
	for(int j=i-1;j>=0;j--)
	{
		cout<<cnt[j]<<" ";
	}
	return 0;
	
	/*string s;
	getline(cin,s);
	for(int i=0;i<s.size();i++)
	{
		cout<<s[i]<<" ";
	}
	cout<<endl;
	return 0;*/
}

simple problem

给定n个数ai及m个询问。
每次询问一段区间的和。
O(n+m)

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	int a[n+5];
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	int f[n+5];
	f[0]=0;
	for(int i=1;i<=n;i++)
	{
		f[i]=f[i-1]+a[i];
	}
	int A,B;
	for(int i=1;i<=m;i++)
	{
		cin>>A>>B;
		cout<<f[B]-f[A-1]<<endl;
	}
	return 0;
}

simple problem exp

给定一个nn的矩阵及m个询问,每次询问一个子矩阵的和
O(n
n+m)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	int a[10000][10000];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
		}
	int f[100000];
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-1]+a[i][j];
		}
	}
	for(int i=1;i<=m;i++){
		cin>>x>>y>>p>>q;
		cout<<f[p][q]-f[x-1][q]-f[p][y-1]+f[x-1][y-1]<<endl;
	}
	return 0;
}

最大子矩阵

n^6

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int a[100][100];
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			cin>>a[i][j];
		}
	int sum=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			for(int l=i;l<n;l++)
				for(int r=j;r<n;r++)
					for(int m=i;m<l;m++)
						for(int n=j;n<r;n++)
						{
							sum+=a[m][j];
						}
	cout<<sum<<endl;
	return 0;
}

1*n的矩阵的最大子矩阵

(洛谷 P1115 最大字段和)

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
using namespace std;

int a[200005],f[200005],g[200005];

int main(){
	int n;
	cin>>n;
	f[0]=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		f[i]=f[i-1]+a[i];
	}
	g[1]=a[1];
	int minn=min(0,f[1]);
	for(int i=2;i<=n;i++){
		g[i]=f[i]-minn;
		minn=min(minn,f[i]);
	}
	int ans=-11111;
	for(int i=1;i<=n;i++){
		ans=max(ans,g[i]);
	}
	cout<<ans<<endl;
	return 0; 
}

圈地运动

n^3(未检验)

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
using namespace std;

int main()
{
	int n;
	cin>>n;
	
	int a[n+5][n+5];         //存数组 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
		
	int g[n+5][n+5];         //求第j列中第一行到第n行的和 
	int tot=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			tot=a[j][i]+tot;
			g[j][i]=tot;
		}
	}
	/*
	int f[1000][1000];       //存数组的前缀和 
	f[0][0]=0;
	int sum=0;
	for(int i;i<=n;i++)
		for(int j=1;j<=n;j++){
			for(int m=i;m>=1;m--)
				for(int n=j;n>=1;n--){
					sum=sum+a[i][j];
				}
			f[i][j]=sum;
		}
	*/ 
	int b[1000];            //求最大矩阵和 
	int MIN[10000];
	int f[1000000];
	f[0]=0;
	int ans=-10000;
	for(int d=1;d<=n;d++)
		for(int u=d;u<=n;u++){
			for(int i=1;i<=n;i++) b[i]=g[u][i]-g[d-1][i];
			for(int j=1;j<=n;j++) f[j]=f[j-1]+b[j];
			MIN[0]=-10000;
			for(int k=1;k<=n;k++) MIN[k]=min(MIN[k-1],f[k]);
			for(int l=1;l<=n;l++) ans=max(ans,f[l]-MIN[l-1]);
		}
	
	cout<<ans<<endl;
	return 0;
}

对手圈地运动

给定一个n*n的矩阵,你的对手在找一个最大子矩阵,并且他知道n^3的做法。
你可以改变其中至多一个数字变成k,让你的对手找到的最大子矩阵尽可能小
(不会)

洛谷 P1058 立体图

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstdio>
using namespace std;
const int n=10000;
char ans[n][n];
int a[n][n];

void drw(int x,int y){
	//zm
	ans[x][y]=ans[x+4][y]=ans[x][y+3]=ans[x+4][y+3]='+';
	for(int i=x+1;i<x+4;i++){
		ans[i][y]='-';
		ans[i][y+3]='-';
	}
	for(int i=y+1;i<y+3;i++){
		ans[x][i]='|';
		ans[x+4][i]='|';
	}
	for(int i=y+1;i<y+3;i++){
		for(int j=x+1;j<x+4;j++){
			ans[j][i]=' ';
		}
	}
	//right
	ans[x+5][y+1]='/';
	ans[x+5][y+2]=ans[x+5][y+3]=' ';
	ans[x+6][y+2]='+';
	ans[x+6][y+3]=ans[x+6][y+4]='|';
	//up
	ans[x+1][y+4]=ans[x+5][y+4]='/';
	for(int i=x+2;i<=x+4;i++){
		ans[i][y+4]=' ';
	}
	ans[x+2][y+5]=ans[x+6][y+5]='+';
	for(int i=x+3;i<=x+5;i++){
		ans[i][y+5]='-';
	}
}

void cinout(int x,int y){
	for(int i=y;i>=0;i--){
		for(int j=0;j<=x;j++){
			if(!ans[j][i]){
				putchar('.');
			}
			else putchar(ans[j][i]);
		}
		cout<<endl;
	}
}

int main(){
	int m,n;
	cin>>m>>n;
	for(int i=m-1;i>=0;i--){
		for(int j=0;j<n;j++){
			cin>>a[j][i];
		}
	}
	int mx=0,my=0;
	for(int y=m-1;y>=0;y--){
		for(int x=0;x<n;x++){
			for(int z=0;z<=a[x][y]-1;z++){
				mx=max(mx,2*y+4*x+6);
				my=max(my,2*y+3*z+5);
				drw(2*y+4*x,2*y+3*z);
			}
		}
	}
	cinout(mx,my);
	return 0;
}

二分大法

有n个数ai,有m次询问,每次询问一个数在这n个数中排第几

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	int a[n+5];
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	sort(a,a+n);
	int l,r;
	int mid=(a[0]+a[n-1])/2;
	for(int i=0;i<m;i++)
	{
		cin>>k;
		while(l<=r)
		{
			if(a[mid]==k) return mid;
			else if(k>mid)
			{
				l=mid+1;
			}
			else r=mid-1;
			mid=(l+r)/2;
		}
	}
	cout<<mid<<endl;
	return 0;
}

搜索

有n件物品,每件物品可选可不选,枚举所有方案。

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
int sum=0;
int a[1000000];
int n;
using namespace std;

void dfs(int x){
	if(x==n+1){
		sum++;
		return ;
	}
	a[x]=1;
	dfs(x+1);
	a[x]=0;
	dfs(x+1);
} 

}
int main(){
	cin >> n;
	dfs(1);
	cout<<sum<<endl;
	return 0;
}

k‘th number

有n个数,共2^n个子集,一个子集的值看做其所有数的和,求第k大的子集。n<=40

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int ans[10000];
int tot=0;
int n,k;
int a[100005];

void dfs(int x,int y){
	if(x==n+1) {
		tot++;
		ans[tot]=y;
		return;
	}
	dfs(x+1,y+a[x]);
	dfs(x+1,y);
}

int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	dfs(1,0);
	sort(ans+1,ans+1+n);
	cout<<ans[k];
	return 0;
}

0/1背包问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<iomanip>
#include<algorithm>
using namespace std;

int n,m;
int ans;
int w[100000],v[100000];

void dfs(int x,int y,int z){
	if(x==n+1){
		if(y<=m){
			ans=max(ans,z);
			return;
		}
	}
	for(int i=1;i<=(m-y)/w[x];i++){
		dfs(x+1,y+w[i]*i,z+v[i]*i);
	}
}

int main(){
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	dfs(1,0,0);
	cout<<ans<<endl;
} 

0/1背包问题优化

0/1/2背包问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<iomanip>
#include<algorithm>
using namespace std;

int n,m;
int ans;
int w[100000],v[100000];

void dfs(int x,int y,int z){
	if(x==n+1){
		if(y<=m){
			ans=max(ans,z);
			return;
		}
	}
	for(int i=1;i<=2;i++){
		dfs(x+1,y+w[i]*i,z+v[i]*i);
	}
}

int main(){
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	dfs(1,0,0);
	cout<<ans<<endl;
} 

0/1/2/3/…背包问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<iomanip>
#include<algorithm>
using namespace std;

int n,m;
int ans;
int w[100000],v[100000],k[1000000];

void dfs(int x,int y,int z){
	if(x==n+1){
		if(y<=m){
			ans=max(ans,z);
			return;
		}
	}
	for(int i=1;i<=k[x];i++){
		dfs(x+1,y+w[i]*i,z+v[i]*i);
	}
}

int main(){
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i]>>k[i];
	}
	dfs(1,0,0);
	cout<<ans<<endl;
} 

快速幂

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
using namespace std;

int f[10000];
int c[10000];
int cnt=1;
int ans=1;

int ksm(int x,int y,int z){
	if(y==0) return 0;
	while(y){
		if(y%2==1){
			ans=ans*x%z;
		}
		x=x*x%z;
		y=y/2;
	}
	return ans;
}

int main(){
	int a,b,p;
	cin>>a>>b>>p;
	ksm(a,b);
	f[0]=a;
	int c=1;
	for(int i=1;i<=b;i++){
		c=c*a;
	}
	cout<<c%p<<" "<<ans%p<<endl;
	return 0;
}

洛谷 P1965 转圈游戏

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

int ans=1;

int ksm(int x,int y,int z){
	if(y==0) return 0;
	while(y){
		if(y%2==1){
			ans=ans*x%z;
		}
		x=x*x%z;
		y=y/2;
	}
	return ans;
}

int main(){
	int n,m,k,x;
	cin>>n>>m>>k>>x;
	cout<<(x+ksm(10,k,n)*m)%n<<endl;
	return 0;
}

洛谷 P1880 石子合并

dp

洛谷 P1123 取数游戏

#include<iostream>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<algorithm>
using namespace std;

int ans=0;
int u[1000][10000]={0};
int n,m;
int f[1000][1000];

void dfs(int a,int b,int c){
    if(a>n){
        ans=max(ans,c);
        return ;
    }
    int nxta=a,nxtb=b+1;
    if(nxtb>m){
        nxtb=1;
        nxta=a+1;
    }
    if(!u[a+1][b+1]&&!u[a+1][b]&&!u[a][b+1]&&!u[a-1][b+1]&&!u[a+1][b-1]&&!u[a-1][b]&&!u[a][b-1]&&!u[a-1][b-1]){
        u[a][b]=1;
        dfs(nxta,nxtb,c+f[a][b]);
        u[a][b]=0;
    }
    dfs(nxta,nxtb,c);
}

int main(){
    int t;
    cin>>t;
    
    
    for(int i=1;i<=t;i++){
        cin>>n>>m;
        for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++){
                cin>>f[j][k];
            }
        dfs(1,1,0);
        cout<<ans<<endl;
        ans=0;
    }
    return 0;
} 

取区间游戏

有n个区间,第i个区间形如【li,ri】
要求选择最多的区间,使任意两个区间都互相不重叠

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
using namespace std;

struct qj{
	int l;
	int r;
}a[200001];

int cmp(const qj &s1,const qj &s2){
	return s1.r<s2.r;
}

int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].l>>a[i].r;
	}
	sort(a+1,a+1+n,cmp);
	int ans=-111;
	int tot=0;
	for(int i=1;i<=n;i++){
		if(a[i].l>ans){
			ans=a[i].r;
			tot++;
		}
	}
	cout<<tot<<endl;
	return 0;
}

洛谷 P1650 田忌赛马

特殊的二分图最佳匹配

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值