暴力枚举 洛谷入门

问题一:
P2241 统计方形(数据加强版)

题目描述
有一个 n×m 方格的棋盘,求其方格包含多少正方形、长方形(不包含正方形)。

输入格式
一行,两个正整数 n≤5000,m≤5000)。

输出格式
一行,两个正整数,分别表示方格包含多少正方形、长方形(不包含正方形)。

输入输出样例
输入
2 3
输出
8 10

问题分析:

  1. 智商不配做暴力枚举了,不知道为什么暴力枚举的题就算简单我也不会,这题本质找规律,这种题目我都不擅长…
  2. 但要注意的点是,题目虽然是int类型的m、n,但多次相乘要让m、n超出int界限,所以要开long long

正确代码:

#include <bits/stdc++.h>
using namespace std;
long long squ;
long long rec;
int main()
{
	long long m,n;
	cin>>n>>m;
	
//分别统计边长从1~min(m,n)的正方形 
	
	for(long long i=1;i<=m;i++)
	{
		for(long long j=1;j<=n;j++)
		{
			if(i==j)
			{
				squ+=(m-(i-1))*(n-(i-1));
			}
			else{
				rec+=(m-(i-1))*(n-(j-1));
			}
		}
	}
	cout<<squ<<" "<<rec;
}

问题2:
P2089 烤鸡
走这

问题分析:

  1. 所有人除了我都会做的烤鸡
  2. 该题目声明10个变量for循环即可

正确代码:

#include <bits/stdc++.h>
using namespace std;
/*只用一个变量表示十种调料的状态是不可能的,因此,每个调料都要用变量表示*/
int a,b,c,d,e,f,g,h,i,j,k;//k用来记录方案个数 
/*可以用一个数组,每个数组元素存储一个调料的状态*/
/*也可以在循环一次,二次循环分别输出方案数和调料各为多少*/
/*主要就在10个变量的创建每个从1~3变化,所以用一个变量代表调料,一个代表调料种类是不现实的*/
int tiao[1000000];
int m;//记录数组下标 
int main()//因为输入的n是正整数,所以不用从0开始 
{
	int n;
	cin>>n; 
	for(a=1;a<=3;a++)//表示调料a从1~3
	{
		for(b=1;b<=3;b++)
		{
			for(c=1;c<=3;c++)
			{
				for(d=1;d<=3;d++)
				{
					for(e=1;e<=3;e++)
					{
						for(f=1;f<=3;f++)
						{
							for(g=1;g<=3;g++)
							{
								for(h=1;h<=3;h++)
								{
									for(i=1;i<=3;i++)
									{
										for(j=1;j<=3;j++)
										{
											if(a+b+c+d+e+f+g+h+i+j==n)
											{
												k++;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	cout<<k<<endl;
	for(a=1;a<=3;a++)//表示调料a从1~3
	{
		for(b=1;b<=3;b++)
		{
			for(c=1;c<=3;c++)
			{
				for(d=1;d<=3;d++)
				{
					for(e=1;e<=3;e++)
					{
						for(f=1;f<=3;f++)
						{
							for(g=1;g<=3;g++)
							{
								for(h=1;h<=3;h++)
								{
									for(i=1;i<=3;i++)
									{
										for(j=1;j<=3;j++)
										{
											if(a+b+c+d+e+f+g+h+i+j==n)
											{
												cout<<a<<" ";  
                                                cout<<b<<" ";  
                                                cout<<c<<" ";  
                                                cout<<d<<" ";  
                                                cout<<e<<" ";  
                                                cout<<f<<" ";  
                                                cout<<g<<" ";  
                                                cout<<h<<" ";  
                                                cout<<i<<" ";  
                                                cout<<j<<endl;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
} 

问题三:
P1618 三连击(升级版)
走这

问题分析:

  1. 这题我的想法是用9个数字排列组合,有这种想法但是不会做…
  2. 看了题解,发现直接列举三位数即可
  3. 再利用条件可以减少枚举的量

正确代码:

#include <bits/stdc++.h>
using namespace std;
int nums[10];
/*可以直接列举三位数,这样就不用考虑不同的组合*/
bool Divide(int i)
{
	nums[i%10]++;
	if(nums[i%10]>1)
	{
		return false;
	}
	nums[i/10%10]++;
	if(nums[i/10%10]>1)
	{
		return false;
	}
	nums[i/100]++;
	if(nums[i/100]>1||(i/100)>=10)
	{
		return false;
	}
	return true;
}
int main()
{
	int flag = 0;
	int A,B,C;
	cin>>A>>B>>C;
//	int j,k;
//	stringstream ss;
    
	for(int i=123;i<=987;i++)
	{
		nums[0] = 2;
	   //通过判断条件直接求出另外两个三位数
	    if(Divide(i)&&Divide(i/A*B)&&(i%A==0)&&Divide(i/A*C))
	    {
	    	cout<<i<<" "<<i/A*B<<" "<<i/A*C<<endl;
	    	flag = 1;
		}
		else{
			memset(nums,0,sizeof(nums));
		}
	}
	if(flag==0)
	{
		cout<<"No!!!";
	}
}

问题四:
P3392 涂国旗
走这

问题分析:

  1. 又是除了我所有人都会的涂国旗,一开始看错题目,以为可以间隔涂色= =
  2. 这里直接枚举每个颜色所占的行数即可

正确代码:

#include <bits/stdc++.h>
using namespace std;
int w[51];//这样就不用分别统计每行要涂的个数并且比较 
int b[51];
int r[51];
int n,m;
/*分别表示把前i行涂成w\b\r三种颜色需要花费的格子数*/
/*
1~i行是白色 i+1~j是蓝色 j+1~N是红色
所以需要花费的格子数是 w(i)+b(j)-b(i)+r(N)-r(j) 
*/
string s;
int check(char c)
{
	int res=0;
	for(int i=0;i<m;i++)
	{
		if(s[i]!=c)
		{
			res++;
		}
	}
	return res;
}
int main()
{
	cin>>n>>m;
//	int res;
	int less = 100000;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		w[i]=check('W')+w[i-1];//因为是表示前i行所以要加前面的 
		b[i]=check('B')+b[i-1];
		r[i]=check('R')+r[i-1];
	} //w(i)+b(j)-b(i)+r(N)-r(j) i必须>=1,j<N且j必须比i大
	//此处需要枚举所有i、j的可能性
	for(int i=1;i<n-1;i++)//i 从1开始表示只涂一行 
	{
		for(int j=i+1;j<n;j++)
		{
			less = min(w[i]+b[j]-b[i]+r[n]-r[j],less);
		}
	} 
	cout<<less;
}

这里大佬的代码进行了预处理,我属实想不到
先统计每行涂成其他颜色需要花费的格子数,在枚举比较

问题五:
P1149 火柴棒等式
走这

问题分析:

  1. 压根不会,看了大佬的代码才懂,这里可以直接枚举和深度搜索两种方式
  2. 直接枚举将24根火柴最多能表示的数字作为界限,求出1到界限所有数分别用的火柴数,在循环枚举加数即可

暴力枚举的代码:

#include <cstdio>
using namespace std;
/*最多24根火柴棒,考虑一下极限,如果只有一个加数,那么这个加数最小是?
枚举0~这个最小数要用的火柴棒,将它们记录在一个数组里
然后再通过枚举0~最小数(加数、被加数、和)的火柴棒之和,看是否会是n 
*/
int c[10] = {6,2,5,5,4,5,6,3,7,6};//通过基础的0~9可以求出其他的十位、百位等数 
int a[3200] = {6};//用此数组,记录0~最小数所有数要用的火柴数 
int j;
int cnt;
int n;
int main()//先给0位赋值,那么就不需要在循环里判断是否为0 
{
	scanf("%d",&n);
	/*先求出1~最小数的火柴棒*/
	for(int i=1;i<=2000;i++)//
	{
		/*如果是大数,需要将每一位分解,再用每一位的火柴数相加*/
		j = i;
		while(j>=1)//所以除了j自己等于0的情况,不可能有==0的情况 
		{
			a[i] = c[j%10]+ a[i];//加完后,a[i]里面的值就是前位火柴数的总和 
		//	k = j%10; 只有j%10才能得到位数上的0 
			j = j/10;//不用担心位数上有0的问题,j/10==0不可能是位数上的0,位数上的0前面必然还有数字 
		}//如果要得到j的每一位,那么就要让循环终止条件为j=0 
	}
	for(int i=0;i<=1000;i++)
	{
		//再用一个循环表示另一个加数
		for(int j=0;j<=1000;j++)
		{
			if(a[i]+a[j]+a[i+j]+4==n)//如果i=1000=j,i+j不弄2000就没有值 
			{
				cnt++; 
			}
		 } 
	 } 
	 printf("%d",cnt);
	 return 0; 
} 

做题时一直想非个位数将其分解后每一位如何储存,没想到直接用赋值前的a数组存储,我的智商不配刷洛谷

也可以用dfs求解每个加数,将符合条件的加上:
正确代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int a[2025]= {6, 2, 5, 5, 4, 5, 6, 3, 7,6};
int b[4];//存储等式的数字 
int cnt;
void dfs(int s)//用来记录收到了第几个加数 
{
	for(int i=0;i<=1000;i++)//用一个循环即可寻找加数 
	{
		if(n-a[i]>=0)
		{
			b[s] = i;
		n-=a[i];
		if(s==3)//先搜索3个数 
		{
			if(b[1]+b[2]==b[3]&&n==0)
			{
				cnt++;
			}
		}else{
			dfs(s+1);
			//搜寻完后回溯
		}
		n+=a[i];
		}
	}
}
int main()
{
	cin>>n;
	n-=4;
	/*因为要搜索哪些符合条件,所以要先将火柴棒数组赋值*/
	for(int i=10;i<=1000;i++)
	{
		a[i] = a[i%10]+a[i/10];//大佬的奇妙思维,我是真的想不到 
	} 
	dfs(1);
	cout<<cnt;
}

暴力枚举基本上很多题都要用到dfs,这里先将我这个菜鸡不会做的暴力枚举题列出,dfs等题目再做点,最后来个总结

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值