这道题的思路是:遍历每一个点,然后以这个点为起点,遍历这个点的下方和右边,如果k个满足条件的点,就使sum+1.。。
#include<iostream>
#include<cstdio>
using namespace std;
char map[1000][1000];
long long int sum = 0;
int R, C, K;
void dfs(int i,int j)
{
if (i > R || j > C)
return;
if (map[i][j] == '.')
{
int temp1 = i;
int temp2 = j;
int x;
for (x = 1; x <= K; x++) //遍历一下列能不能站人
{
if (map[temp1][temp2] != '.')
break;
else
temp1 += 1;
}
if (x == (K + 1))
sum += 1;
temp1 = i; //恢复状态
for (x = 1; x <= K; x++) //遍历一下列能不能站人
{
if (map[temp1][temp2] != '.')
break;
else
temp2 += 1;
}
if (x == (K + 1))
sum += 1;
}
if (i + 1 > R)
{
dfs(1, j+1);
}
else
{
dfs(i+1, j);
}
}
int main()
{
cin >> R >> C >> K;
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++)
cin >> map[i][j];
dfs(1, 1);
if (k == 1) //如果k==1,那么会重复运算,要除以2
sum /= 2;
cout << sum;
return 0;
}
- 写这个题的方法有很多,其中可以暴力解决且AC的就是打表法,关于打表法明天会写一个专题专门来介绍。
- 那么正常来说,如果对[a,b]区间每一个数进行枚举,然后判断是不是回文,再判断是不是质数,这样的方法肯定是超时的,毫无技巧可言。那么将毫无技巧可言的代码也放上来。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int arr[10000];
int R = 0;
int judge(int a) //判断素数
{
if (a == 2)
return 1;
for (int i = 2; i <= sqrt(a); i++)
{
if (a % i == 0) //不是素数
return 0;
}
return 1;
}
void exchange_string(int a) //数字转换成字符串
{
memset(arr, 0, sizeof(arr));
int temp = 10;
int i = 0;
while (a > 0)
{
arr[i] = a - a / temp*temp;
a = a / temp;
R += 1;
i += 1;
}
}
int judge1(int a) //判断回文
{
//将a导进来,然后将a转换成字符串的形式,然后用双指针遍历。
exchange_string(a);
int left = 0;
int right = R - 1; //R用过就要恢复状态
R = 0;
while (right > left)
{
if (arr[left] == arr[right])
{
left += 1;
right -= 1;
}
else
break;
}
//有两种情况,要么left指针等于right指针,这是回文串是奇数的时候。如果回文串是偶数,那么right再left左边一个单位
if (right == left || (right + 1) == left)
return 1;
else
return 0;
}
int main()
{
int a, b;
cin >> a >> b; //找出[a,b]所有的回文数,且该数是质数
for (int i = a; i <= b; i++)
{
if (judge(i) == 1) //先判断质数
{
if(judge1(i)==1) //再判断回文,实际上先判断回文快一点
printf("%d\n", i);
}
}
return 0;
}
这个代码的分数是66分,离100分ac还差很多。但是证明这个思路没有错,关键就是怎样去减少循环的次数,这也是这道题的精髓!
- 位数是偶数的回文数一定不是素数(质数)
- 质数一定不能被2整除,即奇数不一定是质数,但偶数一定不是质数
那么通过这两点的认知,我们就可以很容易写出更简单的代码
首先,我们要知道怎么去判断质数,这里先介绍最简单的方法
bool judge_sushu(int target)
{
if (target == 2) //如果是2,那就是素数,直接返回1,
return true;
for (int i = 2; i <= sqrt(target); i++)
{
if (target % i == 0) //如果可以被i整除,说明这个数不是素数,返回false
return false;
}
return true;
}
然后要知道怎么判断回文
bool judfe_huiwen(int target) //判断回文
{
int temp = target;
int re_target = 0;
while (temp > 0)
{
re_target = re_target * 10 + temp%10; //取temp最后一个数字,反着取给re_target,自己去拿12321模拟一下过程
temp /= 10;
}
if (target == re_target) //如果正的等于反的数,那么就是回文数
return true;
else //否则不是
return false;
}
再一个,偶数位的回文串都不是素数。
bool judge_weishu(int target)
{
int k = 1;
while (target > 0) //计算位数
{
if (target / 10 >= 1)
k += 1;
target /= 10;
}
if (k % 2 == 0&&k>2) //如果位数是偶数,那么返回false,k>2是因为11也是质数,要特殊处理
return false;
else //否则返回true
return true;
}
知道这些代码的原理,那么可以写出完整的程序了,见下面:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
bool judge_sushu(int target)
{
if (target == 2) //如果是2,那就是素数,直接返回1,
return true;
for (int i = 2; i <= sqrt(target); i++)
{
if (target % i == 0) //如果可以被i整除,说明这个数不是素数,返回false
return false;
}
return true;
}
bool judge_huiwen(int target) //判断回文
{
int temp = target;
int re_target = 0;
while (temp > 0)
{
re_target = re_target * 10 + temp%10; //取temp最后一个数字,反着取给re_target,自己去拿12321模拟一下过程
temp /= 10;
}
if (target == re_target)
return true;
else
return false;
}
bool judge_weishu(int target)
{
int k = 1;
while (target > 0) //计算位数
{
if (target / 10 >= 1)
k += 1;
target /= 10;
}
if (k % 2 == 0&&k>2) //如果位数是偶数,那么返回false
return false;
else //否则返回true
return true;
}
int main()
{
int a, b;
cin >> a >> b;
b = (b < 9999999) ? b : 9999999; //因为最大到9999999,因为题目最大位1亿,所以最大的回文99999999,而千万级别又是偶数位,所以所有千万级别都要排除,所以回文的范围是百万级别,所以最大是9999999。
if (a % 2 == 0)//先找第一个奇数,这一步的目的就是因为质数不能被2整除,后面遍历的时候从一个奇数开始,然后每次遍历+2
a = a + 1;
for (int i = a; i <= b; i+=2)
{
if (judge_huiwen(i) == true) //先判断回文
{
if (judge_weishu(i) == true)
{
if (judge_sushu(i) == true)
{
printf("%d\n", i);
}
}
}
}
return 0;
}
这样已经可以ac满分了,但是有没有更好的方法呢?答案是有的。这要用到一个数学小理论:
埃拉托斯特尼筛选法,我们简单一点,叫“埃氏筛选法”
埃式筛选法:如果一个数 x 已经遍历过,不管x是不是素数,它的倍数一定不是素数。
比如:2是素数,那么4,6,8,10.。。。。2n都不是素数
又比如:3是素数,那么6,9,13,15,18,21,,,,3n都不是素数。
很好理解,那么根据这个原理,我们可以很容易写出代码
void judge_sushu(int a, int b)
{
//先假设所有的数都是不是素数。那么这些数的倍数一定不是素数。也就是说,先把大于等于2的值都赋值0,代表都是合数
//然后把他们的倍数置为1,那么之后遍历for循环的时候,只要prime[i]==1,那么一定不是质数,但是prime[i]==0是不是质数还不确定,所以仍然
//需要判断,所以这里假设prime[i]是合数不是因为它真的是合数,因为不管它是不是合数,他的倍数一定不是质数。而且后面的判断也只看prime[i]==1.
for (int i = a; i <= b; i++)
{
if (prime[i] == 0)
{
for (int j = i * 2; j <= b; j = j + i)
prime[j] = 1;
}
}
}
将这个代码加入程序,也可以ac,且时间更快,属于空间换时间了。毕竟一个char数组很大
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
char prime[100000000];
bool judge_sushu(int target)
{
if (target == 2) //如果是2,那就是素数,直接返回1,
return true;
for (int i = 2; i <= sqrt(target); i++)
{
if (target % i == 0) //如果可以被i整除,说明这个数不是素数,返回false
return false;
}
return true;
}
void judge_sushu_1(int a, int b)
{
//先假设所有的数都是不是素数。那么这些数的倍数一定不是素数。也就是说,先把大于等于2的值都赋值0,代表都是合数
//然后把他们的倍数置为1,那么之后遍历for循环的时候,只要prime[i]==1,那么一定不是质数,但是prime[i]==0是不是质数还不确定,所以仍然
//需要判断,所以这里假设prime[i]是合数不是因为它真的是合数,因为不管它是不是合数,他的倍数一定不是质数。而且后面的判断也只看prime[i]==1.
for (int i = a; i <= b; i++)
{
if (prime[i] == 0)
{
for (int j = i * 2; j <= b; j = j + i)
prime[j] = 1;
}
}
}
bool judge_huiwen(int target) //判断回文
{
int temp = target;
int re_target = 0;
while (temp > 0)
{
re_target = re_target * 10 + temp%10; //取temp最后一个数字,反着取给re_target,自己去拿12321模拟一下过程
temp /= 10;
}
if (target == re_target)
return true;
else
return false;
}
bool judge_weishu(int target)
{
int k = 1;
while (target > 0) //计算位数
{
if (target / 10 >= 1)
k += 1;
target /= 10;
}
if (k % 2 == 0&&k>2) //如果位数是偶数,那么返回false
return false;
else //否则返回true
return true;
}
int main()
{
int a, b;
cin >> a >> b;
b = (b < 9999999) ? b : 9999999; //因为最大到9999999
judge_sushu_1(a, b);
if (a % 2 == 0)//先找第一个奇数,这一步的目的就是因为质数不能被2整除,后面遍历的时候从一个奇数开始,然后每次遍历+2
a = a + 1;
for (int i = a; i <= b; i+=2)
{
if (prime[i] == 1) //如果是合数,跳出
continue;
if (judge_huiwen(i) == true) //先判断回文
{
if (judge_weishu(i) == true)
{
if (judge_sushu(i) == true) //这一步还是不能少,因为prime[i]==1代表是合数,但是prime[1]==0不代表就是素数。一定搞清楚
{
printf("%d\n", i);
}
}
}
}
return 0;
}
这道题用两种方法都可以
1:dfs
2.不用dfs
1.首先,我们知道,所有的由火柴拼出来的数字归根结底都是0-9这10个数字,那么就先预处理一下这10个数字
int a[10] = { 6,2,5,5,4,5,6,3,7,6}; //摆放0-9需要的火柴数
2.这个时候有人问了,那10以后的数怎么办呢?这就是这道题的关键,还要对大于10的数进行处理(这里以2000为上限,大一点好)
for (int i = 1; i <= 2000; i++) //把数字转换成火柴数
{
int temp = i; //把i先临时存进一个遍历temp
while (temp > 0) //然后一位一位计算需要多少火柴
{
num[i] += a[temp % 10]; //temp%10就是当前temp的最后一个数字
temp /= 10;
}
}
完整代码:
#include<iostream>
#include<cstdio>
using namespace std;
int a[10] = { 6,2,5,5,4,5,6,3,7,6}; //摆放0-9需要的火柴数
int num[2001];
int path[10];
int n;
int sum = 0;
/*void dfs(int k) //回溯
{
if (k > 3)
{
if (path[1] + path[2] == path[3]&&(n-4)==0)
{
sum += 1;
}
return;
}
for (int i = 0; i <= 1000; i++)
{
if (n - num[i] >= 4) //还有火柴
{
path[k] = i;
n = n - num[i];
dfs(k + 1);
n = n + num[i]; //恢复状态
}
}
}*/
int main()
{
cin >> n;
num[0] = 6;
for (int i = 1; i <= 2000; i++) //先把数字转换成火柴数
{
int temp = i;
while (temp > 0)
{
num[i] += a[temp % 10];
temp /= 10;
}
}
//dfs(1);
for (int i = 0; i <= 1000; i++)
{
for (int j = 0; j <= 1000; j++)
{
if (num[i] + num[j] + num[i + j] == n - 4)
sum += 1;
}
}
cout << sum;
}
用递归的话就把上面代码灰色的注释取消,让主函数的for循环注释掉就好了。(dfs(1)下面的for循环注释掉)
这道题的思路是枚举所有长度,以这个长度为长边,然后再以这个长边为基础取取短边。这里有几个基础
1.要知道组合的实现
int C(int n, int m) //求n个数里面拿m个数有多少种组合
{
int ans = 1;
for (int i =1; i<=m; i++)
{
ans = ans * n / i;
n -= 1;
}
return ans;
}
这个代码的原理就是高中数学学的组合。
c(n,m)n在下面,m在上面,比如c(6,2),m是2,n是6。
c(6,2)等于(65)/(12)…公式是:(n* n-1 * n-2 * … * n-m+1)/(m!);
那么有人会问,因为在c里面‘/’是取整除法,万一上面的代码出现小数岂不是出错?这个问题问的很好,非常细心。那么下面就这个问题来证明一下。
首先,我们发现,任意两个连续的数字,其中必有一个可以被2整除。
假设两个连续的数分别为m,m+1.
m%2只有两种情况。
1.m%2=0,则m是2的倍数,m可以被2整除
2.m%2=1,则m=2*k+1,则m+1=2 * k+1+1=2 * (k+1),则m+1是2的倍数,m+1可以被2整除
由此推导,那么3个连续的数必然有一个数可以被3整除
假设三个连续的数分别是m,m+1,m+2;
m%3只有三种可能
1.m%3=0,则m是3的倍数,m可以被3整除
2.m%3=1,则m=3 * k+1,则m+2=3 * k+3=3 * (k+1).则m+2可以被3整除
3.m%3=2,则m=3 * k+2,则m+1=3 * k+3=3 * (k+1),则m+1可以被3整除
那么大胆推导一下,n个连续的数相乘一定可以被n整除!
但是这还不够,再想一想。
n个连续的数相乘可以被1到n的所有数整除!
比如三个连续的数即可以被3整除,同时也可以被2整除,也可以被1整除
还是m,m+1,m+2
m%2两种可能。
1.m%2=0,则m可以被2整除
2.m%2=1,则m+1可以被2整除
还不够!还可以推导💥
再大胆猜测
n个连续的数相乘可以被1到n的所有数同时整除!
这个想了半天,还是证明不完整,还是当成结论记吧。。。
知道这个组合的构造就好写了
#include<iostream>
#include<cmath>
#include<cstdio>
#pragma warning (disable:4996)
using namespace std;
int num_count[10000]; //num_count[i]==j代表长度为i的边长有j个
const int kmod = 1e9 + 7;
long long int C(int a, int b) //求a个数里面取b个数德所有组合,当然a要大于等于b,在这道题里面b要么是1,要么是2
{
if (b==1)
return a;
else
return (a * (a - 1) / 2);
}
int main()
{
int n;
int a;
int max = 0; //记录最长的边
int min = 1000000; //记录最短的边
long long int sum = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a);
if (a > max)
max = a;
if (min > a)
min = a;
num_count[a] += 1;
}
for (int i = min; i <= max; i++) //枚举所有长度的边
{
if (num_count[i] >= 2) //如果该长度的边的个数大于2个,那么把这边当作长边
{
//先算出这两条边的所有组合
int k1 = C(num_count[i], 2);
for (int j = min; j <= i / 2; j++) //因为先枚举的长边,那么就再枚举短边,短边有两条,两条之和等于长边
{
if (i - j == j&&num_count[j]>=2)//两条短边一样长且短边有两条以上
{
int k2 = C(num_count[j], 2);
sum = sum + k1 * k2 % kmod;
}
if (i - j != j && num_count[j] >= 1 && num_count[i - j] >= 1)
{
int k2 = C(num_count[j], 1);
int k3 = C(num_count[i - j], 1);
sum = sum + k1 * k2 * k3 % kmod;
}
sum%= kmod;
}
}
}
printf("%lld", sum);
}