暴力枚举相关

本文探讨了暴力枚举、递归方法在解决编程问题中的应用,涉及配料组合、选数、三连击问题的优化、组合输出和涂条纹等实例,以及如何通过递归实现FirstStep和PERKET问题的解法。
摘要由CSDN通过智能技术生成

暴力枚举优化

P2089 烤鸡

直接十个for暴力也可以

用递归简化一些

  1. 有了十种配料并且总配料数正确,则存入一组数据
  2. 总配料超出就不管,看下一个
  3. 循环1~3,配料轮流加
  4. 最后输出存入的所有数据

递归: 结束条件:总调料数达到10,总调味度到达n,就把m2存到m1,
设两个数组一个m1二维数组,存储所有配料可能
一个m2一维数组,存储当前递归符合题意的一种配料组合
最后把总数和m1数组全部输出

#include <bits/stdc++.h> 
using namespace std;

int n,kind,m1[10000][10],m2[10];

void peiliao(int total,int a)
{
	if(a==10)
	{
		if(total==n)
		{
			for(int i=0;i<10;i++) m1[kind][i]=m2[i];
			kind++;
		}
	}
	else if(total>n) ;
	else
		for(int i=1;i<=3;i++)
		{
			m2[a]=i;
			peiliao(total+i,a+1);
		}
}

int main()
{
	cin>>n;
	peiliao(0,0);
	cout<<kind<<endl;
	for(int i=0;i<kind;i++)
	{
		for(int j=0;j<10;j++) cout<<m1[i][j]<<" ";
		cout<<endl;
	}
	return 0;
}

P1036 选数

优化了一下不降原则

for(int i=y;i<=n;i++)从y开始,以确保不会选到前面已经满足的一组数据

#include <bits/stdc++.h>
using namespace std;

int n,k,a[21],s=0,ans=0;
bool st[21];

bool isPrime(int x) {
    if (x<2)
        return false;

    for (int i = 2; i <= sqrt(x); i += 1) {
        if (x % i == 0)
            return false;
    }

    return true;
}

void dfs(int x,int y)
{
	if(x==k)
	{
		if(isPrime(s)) ans++;
		return;
	}
	for(int i=y;i<=n;i++) //注意
	{
		st[i]=false;
		s+=a[i];
		dfs(x+1,i+1);
		s-=a[i];
		st[i]=true;
	}
}

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

P1618 三连击(升级版)

思想:

  1. 数组 num 统计1~9出现次数
  2. 直接用倍数乘每一次i得到三个三位数
  3. 把三个三位数拆分得到1~9出现次数 flag
  4. 注意每次遍历更新 numflag
#include <bits/stdc++.h>

using namespace std;

int num[10],ans;
bool flag=true;

int main()
{
	int a,b,c,A,B,C;
	cin>>a>>b>>c;
	for(int i=1;i*c<=1000;i++)//注意写成i<1000/c会不过
	//循环不从三位数开始,可能倍速abc就是192 384 576这样的极端 
	{
		A=i*a,B=i*b,C=i*c;
		//题目保证A<B<C,因此i*c最大,能进来就已经满足三位数前提了 
		//统计1~9出现次数 
		for(int j=1;j<=3;j++)
		{
			num[A%10]++;
			A/=10;
		}
		for(int j=1;j<=3;j++)
		{
			num[B%10]++;
			B/=10;
		}
		for(int j=1;j<=3;j++)
		{
			num[C%10]++;
			C/=10;
		}
		for(int j=1;j<=9;j++) 
		{
			if(num[j]!=1)
			{
				flag=false;//不满足题意 
				break;
			}
		}
		
		if(flag==true)
		{
			cout<<i*a<<" "<<i*b<<" "<<i*c<<endl;
			ans++;
		}
		else flag=true; //记得更新	
		memset(num,0,sizeof num);
	} 
	if(!ans) cout<<"No!!!";
	return 0;
}

P1157 组合的输出

套板子,注意后面的数一定要比前面的数大

#include <bits/stdc++.h>
using namespace std;

int a[50];
bool st[10];
int n,r;

void print()
{
	for(int i=0;i<r;i++)
		cout<<setw(3)<<a[i];
		cout<<endl;
}

void dfs(int u)
{
	if(u==r) //注意是和r比较不是和n
	{
		print();
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(!st[i]&&i>a[u-1]||u==0)
		{
			a[u]=i;
			st[i]=true;
			dfs(u+1);
			a[u]=0;
			st[i]=false;
		}
	}
}

int main()
{
	cin>>n>>r;
	dfs(0);
	return 0;
 } 

P3392 涂条纹

直接暴力一部分测试点会超时

每一次枚举:

1~i是白色,i+1到 j 行是蓝色, j+1到n行是红色,然后每一种涂法更新最小值

for(int i=1;i<=n-2;i++)
	for(int j=i+1;j<=n-1;j++)
	{
		ans=0;
		for(int k=1;k<=i;k++)
			for(int g=1;g<=m;g++) if(c[k][g]!='W') ans++;
		for(int k=i+1;k<=j;k++)
			for(int g=1;g<=m;g++) if(c[k][g]!='B') ans++;
		for(int k=j+1;k<=n;k++)
			for(int g=1;g<=m;g++) if(c[k][g]!='R') ans++;
		mi=min(ans,mi);	
	 }		 		

优化做法:

开数组w[i],b[i],r[i],分别表示把前i行涂成白、蓝、红需要涂的格子数

设第1行到第i行是白色

第i+1行到第 j 行是蓝色

则第 j+1行到第 n 行是红色

在 **w[i]+b[j]-b[i]+r[n]-r[j] **中

b[j]-b[i]计算第 i+1 行到第 j 行之间的不匹配 'B' 的个数。

r[n]-r[j],计算从第 j+1 行到最后一行的不匹配 'R' 的总数。

#include <bits/stdc++.h>
using namespace std;
int n,m,ans=0x3f3f3f3f,w[51],b[51],r[51];
string s;

int check(char c)
{
	int cnt=0;
	for(int i=0;i<m;i++)
		if(s[i]!=c) cnt++;
	return cnt;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		w[i]=w[i-1]+check('W');
		b[i]=b[i-1]+check('B');
		r[i]=r[i-1]+check('R');
	}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<n;j++) //注意j<n,别等于,r没位置放了
			ans=min(ans,w[i]+b[j]-b[i]+r[n]-r[j]);
	cout<<ans;
	return 0;
}

P3654 First Step

思想:

  1. 只需要判断向右或者向下是否有空位
  2. 注意当 k=1 时横着站和竖着站都一样,会重复计算,要除以2

递归: 结束条件: 连续可站位数==人数,站法加一
不满足空位和边界范围就返回

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
typedef pair<int,int> PII;

int n,m,k,dx[2]={1,0},dy[2]={0,1};
char g[N][N];
int ans;
void dfs(int x,int y,int i,int j)
{
	if(j>k)
	{
		ans++;
		return;
	}
	if(g[x][y]!='.'||x<0||y<0||x>=n||y>=m) return;
	dfs(x+dx[i],y+dy[i],i,j+1);
	return;
}

int main()
{
	cin>>n>>m>>k;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			cin>>g[i][j];
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if(g[i][j]=='.')
				for(int c=0;c<2;c++)
					dfs(i,j,c,1);
	if(k==1) ans/=2;
	cout<<ans;
	return 0; 
}

P2036 PERKET

递归:

结束条件:调用的数量等于总数,不存在清水情况,特判返回重新找

分为添加和不添加两种情况

#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int n,m,ans=0x3f3f3f3f,a[N],b[N];

void dfs(int i,int suan,int ku)
{
	if(i==n)
	{
		if(suan==1&&ku==0) return; //判断清水的情况
		ans=min(ans,abs(suan-ku));
		return;
	}
	dfs(i+1,suan*a[i],ku+b[i]); //添加
	dfs(i+1,suan,ku); //不添加
}

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JaneHan_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值