一:全排列系列
1. 全排列:
Description
输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
Input
n(1≤n≤9)
Output
由1~n组成的所有不重复的数字序列,每行一个序列。(每个数字后面均有一个空格)
Samples
Input Copy
3
Output
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
这个直接就是全排列模板题,直接上代码咯,另外,把库函数的那个全排列代码也放一下
DFS代码:
#include<iostream>
using namespace std;
bool vis[10];
int ans[10],n;
void dfs(int t)
{
if(t==n)
{
for(int i=0;i<n;i++)
{
cout<<ans[i]<<" ";
}
puts("");
return;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
ans[t]=i;
vis[i]=1;
dfs(t+1);
vis[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
库函数代码:
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<string>
typedef long long ll;
using namespace std;
int n;
int main()
{
cin >> n;
string a;
int flag = 0;
for (int i = 0;i < n;i++)
{
a+=i+49;
}
do {
if (flag) cout << endl;
for (int i = 0;i < a.size();i++)
{
cout << a[i]<<" ";
}
flag = 1;
} while (next_permutation(a.begin(), a.end()));
return 0;
}
------------------------------------------------------分割线------------------------------------------------------------------------
2. 组合的输出
Description
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r<=n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你用递归的方法输出所有组合。
例如n=5,r=3,所有组合为:
l 2 3 l 2 4 1 2 5 l 3 4 l 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
Input
一行两个自然数n、r(1<n<21,1<=r<=n)。
Output
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。(每一个数后跟一个空格)
Samples
Input
5 3
Output
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
这个也是个全排列问题,注意一点就是下次搜的时候要大与前一位的数,代码中的体现就是这句
for(int i=ans[t-1]+1;i<=n;i++)
上完整AC代码:
#include<iostream>
#include<cstdio>
using namespace std;
int n,r,ans[22];
bool vis[22];
void dfs(int t)
{
if(t==r+1)
{
for(int i=1;i<=r;i++)
{
cout<<ans[i]<<" ";
}
puts("");
return;
}
for(int i=ans[t-1]+1;i<=n;i++)
{
if(!vis[i])
{
ans[t]=i;
vis[i]=true;
dfs(t+1);
vis[i]=false;
}
}
}
int main()
{
cin>>n>>r;
dfs(1);
return 0;
}
------------------------------------------------------分割线------------------------------------------------------------------------
3. 问题 F: Count Order
题目描述:
We have two permutations P and Q of size N (that is, P and Q are both rearrangements of (1, 2, …, N)).
There are N! possible permutations of size N. Among them, let P and Q be the a-th and b-th
lexicographically smallest permutations, respectively. Find |a−b|.
NotesFor two sequences X and Y, X is said to be lexicographically smaller than Y if and only if there exists an integer k such that Xi=Yi (1≤i<k) and Xk<Yk.Constraints
·2≤N≤8
·P and Q are permutations of size N.
输入
Input is given from Standard Input in the following format:
N
P1 P2 … PN
Q1 Q2 … QN
输出
Print |a−b|.
样例输入
【样例1】
3
1 3 2
3 1 2
【样例2】
8
7 3 5 4 2 1 6 8
3 8 2 5 4 6 7 1
【样例3】
3
1 2 3
1 2 3
样例输出
【样例1】
3
【样例2】
17517
【样例3】
0
提示
样例1解释
There are 6 permutations of size 3: (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2),
and (3, 2, 1). Among them, (1, 3, 2) and (3, 1, 2) come 2-nd and 5-th in lexicographical order,
so the answer is |2−5|=3.
题目大意:给你一个整数n,列出从1到n的整数的所有情况,输入的时候输入其中的两种情况,两种情况可能相同,找出这两种情况在所有情况中按字典序排序后的序号差的绝对值 |m-n|;
解题思路:全排列用深搜解决的板子题,话不多说直接上代码啦。。。
上 AC 代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5;
struct cunchu{
char m[10];
}st[maxn];
bool cmp(cunchu l,cunchu r)
{
if(strcmp(l.m,r.m)!=0)
return strcmp(l.m,r.m)<0;
}
char a[10],test1[10],test2[10];
int n,k=0;
int vis[100]={0};
void dfs(ll t)
{
memset(vis,0,sizeof(vis));
if(t==n)
{
strcpy(st[k++].m,a);
k++;
return;
}
for(int i=t;i<n;i++)
{
if(vis[i]==0)
{
vis[i]=1;
char temp=a[i];a[i]=a[t];a[t]=temp;
dfs(t+1);
temp=a[i];a[i]=a[t];a[t]=temp;
vis[i]=0;
}
}
}
int main()
{
int x,y,i,j;
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(i=0;i<n;i++)
a[i]=i+1+48;
for(i=0;i<n;i++)
{
scanf("%d",&x);
test1[i]=x+48;
}
for(i=0;i<n;i++)
{
scanf("%d",&y);
test2[i]=y+48;
}
dfs(0);
//printf("%d\n",k);
sort(st,st+k,cmp);
int ans1,ans2;
for(ll i=0;i<k;i++)
{
if(strcmp(st[i].m,test1)==0)
{
ans1=i;
}
if(strcmp(st[i].m,test2)==0)
ans2=i;
}
printf("%d",abs(ans1-ans2));
return 0;
}
提议:排序的时候放进结构体里边非常好操作,这也让我尝到了不少甜头,用了很多次,嘿嘿~
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
--------------------------------------------------------分割线------------------------------------------------------------------------
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
二:八皇后系列
1.输出 n 皇后
问题描述:
n-皇后问题是指将 n 个皇后放在 n∗n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数n,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数n。
输出格式
每个解决方案占n行,每行输出一个长度为n的字符串,用来表示完整的棋盘状态。
其中”.”表示某一个位置的方格状态为空,”Q”表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
关于此问题,可根据不同的搜索方式而写出不同的代码,相比之下肯定也是有时间复杂度上的差距的,第一种就是直接中二维表格的第一格开始从左到右,从上到下的常见顺序进行一个一个的搜索;第二种就是上升了一个思维度的,可想每一层或者每一列都只能放一个皇后,进而直接搜索列和行就ok,很显然,第一种方法的复杂度要比第二种的复杂度高,但对于数据范围小的情况下,这两个其实没什么太大的差距,下面我将两种代码都呈上
上代码1(上述所谓的第一种搜索方法):
#include<iostream>
#include<cstdio>
using namespace std;
int n;
char a[10][10];
bool cal[10],dia[20],bdia[20],row[10];//分别表示列,主对角线,反对角线,行
void dfs(int x,int y,int s)
{
if(y==n) y=0,x++;
if(x==n)
{
if(s==n)
{
for(int i=0;i<n;i++)
{
puts(a[i]);
}
puts("");
}
return;
}
///不放皇后
dfs(x,y+1,s);
///放皇后
if(!row[x]&&!cal[y]&&!dia[x+y]&&!bdia[x-y+n])
{
a[x][y]='Q';
row[x]=cal[y]=dia[x+y]=bdia[x-y+n]=true;
dfs(x,y+1,s+1);
row[x]=cal[y]=dia[x+y]=bdia[x-y+n]=false;
a[x][y]='.';
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
a[i][j]='.';
}
}
dfs(0,0,0);
return 0;
}
上代码2(上述所谓的第二种搜索方法):
#include<iostream>
#include<cstdio>
using namespace std;
int n;
char a[10][10];
bool cal[10],dia[10],bdia[10];//分别表示列,主对角线,反对角线
void dfs(int t)
{
if(t==n)
{
for(int i=0;i<n;i++)
{
puts(a[i]);
}
puts("");
return;
}
for(int i=0;i<n;i++)
{
if(!cal[i]&&!dia[t+i]&&!bdia[t-i+n])
{
a[t][i]='Q';
cal[i]=dia[t+i]=bdia[t-i+n]=true;
dfs(t+1);
cal[i]=dia[t+i]=bdia[t-i+n]=false;
a[t][i]='.';
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
a[i][j]='.';
}
}
dfs(0);
return 0;
}
------------------------------------------------------分割线------------------------------------------------------------------------
Description
在N*N的棋盘上放置N个皇后(n<=10)而彼此不受攻击(即在棋盘的任一行,任一列和任一对角线上不能放置2个皇后),编程求解所有的摆放方法。
Input
输入:n
Output
每行输出一种方案,每种方案顺序输出皇后所在的列号,各个数之间有空格隔开。若无方案,则输出no solute!(请不要输出多余空格)
Samples
Input Copy
4
Output
2 4 1 3
3 1 4 2
无非就是加一个数组来记录答案即可
上代码:
#include<iostream>
#include<cstdio>
using namespace std;
int ans[12],n,count1;
bool cal[12],dia[12],bdia[12];
void dfs(int t)
{
if(t==n)
{
for(int i=0;i<n;i++)
{
cout<<ans[i];
if(i!=n-1) cout<<" ";
}
count1++;
puts("");
return;
}
for(int i=1;i<=n;i++)
{
if(!cal[i]&&!dia[t+i]&&!bdia[t-i+n])
{
ans[t]=i;
cal[i]=dia[t+i]=bdia[t-i+n]=true;
dfs(t+1);
cal[i]=dia[t+i]=bdia[t-i+n]=false;
}
}
}
int main()
{
cin>>n;
dfs(0);
if(count1==0) cout<<"no solute!";
return 0;
}
------------------------------------------------------分割线------------------------------------------------------------------------
3.UPC Contest2355 - 2020春季个人训练赛第二十五场 A:分书问题
题目描述
已知有n本书(从1~n编号)和n个人(从1~n编号),每个人都有一个自己喜爱的书的列表,现在请你编写一个程序,设计一种分书方案,使得每个人都能获得一本书,且这本书一定要在他的喜爱列表中。
输入
输入数据共若干行,第一行为一个正整数n(n <= 20),从第2行到第n+1行,每行有n个0或1组成,第k行表示编号为k-1的人对这n本书的喜好列表,0表示不喜欢,1表示喜欢。
输出
输出数据仅一个整数,表示符合条件的分配方案的总数。
样例输入
5
00110
11001
01100
00010
01001
样例输出 Copy
1
一 开始看到这么小的数据范围,确实想到了搜索,但弱鸡的我在比赛时仍然是没把他给解决,赛后在小伙伴的帮助下就明白了,然后一发补题通过,想了想完全就是和 n 皇后一个样子的思路,而且还要比n皇后简单咧,少了对角线和反对角线的两种情况呐,哎,还是没有很好的运用知识呀。
上代码:
#include<iostream>
#include<vector>
#include<string>
#include<cstring>
using namespace std;
string a[25];
bool vis[25];
int n,ans;
void dfs(int t)
{
if(t==n)
{
ans++;
return;
}
for(int i=0;i<n;i++)
{
if(!vis[i]&&a[t][i]=='1')
{
vis[i]=true;
dfs(t+1);
vis[i]=false;
}
}
}
int main()
{
cin>>n;
memset(vis,false,sizeof(vis));
for(int i=0;i<n;i++)
{
cin>>a[i];
}
dfs(0);
cout<<ans<<endl;
return 0;
}
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
--------------------------------------------------------分割线------------------------------------------------------------------------
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
三:记忆化搜索系列
1.SLF的锁屏密码
题目描述
SLF作为一个与众不同的人,他的锁屏密码当然不能和别人一样简简单单的4位密码或者是按个爪印就可以了。
每次SLF想要开启手机时,他需要手机先告诉他一个数N,这个数当然不是一个简简单单数字,它是有特殊含义的:
对给定的N(4≤N≤24),它其实还表示了一个特殊等式-1@2@3@4@5…@N-2@N-1=N,其中字符@可能是加号也可能是减号。
如果SLF想要开启手机,他就要输入有多少个符合条件的这样的等式。
输入
包含一个整数的单独一行。
输出
包含一个整数的单独一行表示满足条件的等式的个数。
样例输入 Copy
8
样例输出 Copy
4
小提醒:不能在sum>n的时候跳出,要在t>n的时候跳出,,因为当sun大于n时如果后面还有数字,再减去后面的数有可能就能得到答案,所以说,你直接sum>n就跳出的话,就少考虑情况了,所以一定就错啦(已做过入坑测试 )但我不知道为啥得到的结果反而偏大,qwq,难以解释,其他的话就是正常操作啦。
上代码:
#include<bits/stdc++.h>
using namespace std;
int ans,n;
int num[30];
void dfs(int t,int sum)
{
//if(sum>n) return;
if(t>n) return;
if(t==n&&sum==n){
ans++;
return;
}
dfs(t+1,sum+num[t]);
dfs(t+1,sum-num[t]);
}
int main()
{
cin>>n;
for(int i=2;i<=n-1;i++)
{
num[i]=i;
}
dfs(2,-1);
cout<<ans<<endl;
return 0;
}
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
--------------------------------------------------------分割线------------------------------------------------------------------------
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
四:迷宫系列
1.Contest2387 - 2020年春混合个人训练第一场 D:迷宫
题目描述
迷宫的管理员们决定在新开始的季节里使用新的墙纸。出于这个目的他们需要一个程序来计算迷宫内墙壁的面积。这就是你即将要做的工作。
我们把这个迷宫用一个NN(3<=N<=33)的矩阵表示。一些矩阵单元包含一个“.”(这代表一个空的方块),另一些矩阵单元包含一个“#”(这代表一个用巨石砌成的石墙占据的方块)。全部方块的大小都为33平方米。
墙壁由迷宫的四周(除了作为迷宫出入口的左上角和右下角以外)以及那些标记为“#”的矩阵单元构成,除此之外没有其他的墙。在输入的矩阵里左上角和右下角永远是一个“.”。你的任务是计算迷宫里可见部分的墙壁的面积。换句话说,就是对迷宫的游客来说墙壁表面可见的部分。注意在两块相邻的石块之间没有空隙,即使两块石块在转角处相接触,我们都认为它们是相邻的。看看图示的例子:迷宫中可见的墙壁都用加粗的线条来描画。所有墙壁的高度都是三米。
输入
输入的第一行包含一个数字N。接下来的N行每行都包含有N个字符。每行描述了迷宫矩阵的一行。每行都只有“.”、“#”这两个字符并都以一个换行符结束。输入里没有任何的空格
输出
你的程序必须输出一个整数,即所需要的壁纸的准确面积。
样例输入
5
…
…##
…#…
…###
…
样例输出
198
看懂这个图的话,基本就能解决这道题了,一开始完全没看懂这个图是嘛意思,后来了解了,就是算一下那个加粗的线段个数,这里需要注意三点,一:入口和出口都要减去两条线段;二:最外围一圈都是墙壁,即都是加粗的线断;三:走不到的点的四周的墙壁不需要处理。然后带着这三点再回头写代码,基本就能A了。
上代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
char a[35][35];
int n;
bool vis[35][35];
int dx[] = { 0,1,0,-1 };
int dy[] = { 1,0,-1,0 };
void dfs(int t1,int t2)
{
if (t1 > n || t2 > n || t1 <= 0 || t2 <= 0 || a[t1][t2] == '#' || vis[t1][t2]) return;
vis[t1][t2] = true;
for (int i = 0;i < 4;i++)
{
int x = t1 + dx[i];
int y = t2 + dy[i];
dfs(x, y);
}
}
int main()
{
memset(vis, false, sizeof vis);
cin >> n;
for (int i = 1;i <= n;i++)
{
cin >> a[i]+1;
}
/*for (int i = 1;i <= n;i++)
{
for (int j = 1;j <=n;j++)
{
cout << a[i][j];
}
cout << endl;
}*/
dfs(1, 1);
dfs(n, n);
int ans = 0;
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= n;j++)
{
if (vis[i][j])
{
for (int k = 0;k < 4;k++)
{
if (!vis[i + dx[k]][j + dy[k]]) ans++;
}
}
}
}
cout << 9 * (ans - 4) << endl;
return 0;
}
------------------------------------------------------分割线------------------------------------------------------------------------
2.Contest2377 - 2020年春混合个人训练第二场 G:迷宫
题目描述
晚上,跑男们来了节目的最后一站:江苏省扬州中学,完成最后一项比赛:撕名牌。撕名牌的地点是一个由n*n房间组成的正方形,每个房间里都有一个数字,表示从这个房间可以通过地道向右或向下穿过几个房间。从左上角开始,如果谁能安全到达右下角就算胜利。
这里4*4的方格中每一格表示进入这个房间时,队员可以向右或向下穿过的房间数。
郑恺是奔跑小王子,当他拿到这张地图时,脸都变绿了,速度再快,进了迷宫一样的房间也是没办法啊,还好参加JSOI2015夏令营的小伙伴都在,你能帮帮他算出从左上角可以到达右下角的路径数目吗?
输入
第一行为一个整数n,表示棋盘的大小。
以下有n行,每行有n个数字(数字与数字之间有一个空格隔开),表示在相应的格子内,棋子可以向右或向下跳跃的格子数。
输出
输出共一行,包含一个数,表示从左上角可以到达右下角的路径数目。
样例输入
4
2 3 3 1
1 2 1 3
1 2 3 1
3 1 1 0
样例输出
3
提示
对于100%的数据,1≤n≤100。
这题思路就是记忆化的搜索路径,对于每一个位置,搜一下该位置右侧和该位置下侧,对于搜得是右侧或下侧多大距离处,那就要看该位置处的值是多少了,是多少,距离就是多少,能够到达目的地的话就让ans++;最后输出ans就是答案。
上代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int a[110][110],n,ans;
bool vis[110][100];
void dfs(int t1,int t2)
{
if (t1 > n || t2 > n) return;
if (t1 == n && t2 == n) {
ans++;
return;
}
if (t1 + a[t1][t2] <= n && !vis[t1 + a[t1][t2]][t2])
{
vis[t1 + a[t1][t2]][t2] = true;
dfs(t1 + a[t1][t2], t2);
vis[t1 + a[t1][t2]][t2] = false;
}
if (t2 + a[t1][t2] <= n && !vis[t1][t2 + a[t1][t2]])
{
vis[t1][t2+a[t1][t2]] = true;
dfs(t1, t2+ a[t1][t2]);
vis[t1][t2 + a[t1][t2]] = false;
}
}
int main()
{
cin >> n;
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= n;j++)
{
cin >> a[i][j];
}
}
dfs(1, 1);
cout << ans << endl;
return 0;
}
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
--------------------------------------------------------分割线------------------------------------------------------------------------
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
五:枚举系列
2020.4.29 - - 星期三
1.Contest2389 - 2020年春混合个人训练第三场 F:种花
题目描述
在机房的生活是如此的寂寞,以至于以will为首的同志们只能够天天上农场种菜来打发时间。
msh日复一日地种着她的玫瑰,will则毫不疲倦地偷着他的花……尽管天天花被偷掉一半,msh始终没有动摇她种花的决心。原来,一个宏伟计划的蓝图早已埋藏在她的心中。
众所周知,农场的花一共有4种颜色,msh喜欢不喜欢老旧的东西,所以,她希望每天种花的方案都不一样。特别地,她也觉得两种一样颜色的花种在相邻的位置会很无聊。现在,她想知道,一共有多少种花的方案。
这里要注意的是,农场的种花的位置是不规则的。因此我们给出一对一对的相邻的位置的关系。
输入
第一行两个数N和M,表示种花的位置的个数和相邻的位置的对数
接下来M行,每行一组数A,B表示A,B相邻
输出
一个数表示染色方法数
样例输入
5 4
1 2
1 3
1 4
1 5
样例输出
324
提示
100%的数据 N<=10,M<=50
一看到涂色问题,首先就想到了组合数,一直就在那考虑用组合数怎么做呀,愣了半天,硬是没想出来,后来补题的时候,咨询了一下dl们,给补了出来,由于数范围比较小,直接dfs枚举所有情况再进行判断即可。同时也明白了个道理:当你在一条路上走不通的时候,有时候换一条路也许会有更好的体验。
上代码:
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
vector<PII> v;
int n,ans,m;
int a[11];
bool chack(int a[])
{
for (auto item : v)
{
if (a[item.first] == a[item.second]) return false;
}
return true;
}
void dfs(int t)
{
if (t > n)
{
if (chack(a)) ans++;
return;
}
for (int i = 1;i <= 4;i++)
{
a[t] = i;
dfs(t + 1);
a[t] = 0;
}
}
int main()
{
cin >> n >> m;
for (int i = 1;i <= m;i++)
{
int x, y;
cin >> x >> y;
v.push_back({ x,y });
}
dfs(1);
cout << ans << endl;
return 0;
}
这个代码由于我用了STL里的动态开辟内存的vector,在数据比较多的情况下,会耗费不少的时间,所以建议最好是用结构体数组来进行替换,替换的话很简单这里就不再附代码了。
2020.4.29 - - 星期三
2.AtCoder Beginner Contest 114 C:755
题目描述
You are given an integer N. Among the integers between 1 and N (inclusive), how many Shichi-Go-San numbers (literally “Seven-Five-Three numbers”) are there?
Here, a Shichi-Go-San number is a positive integer that satisfies the following condition:
When the number is written in base ten, each of the digits 7, 5 and 3 appears at least once, and the other digits never appear.
Constraints
1≤N<109
N is an integer.
输入
Input is given from Standard Input in the following format:
N
输出
Print the number of the Shichi-Go-San numbers between 1 and N (inclusive).
样例输入
575
样例输出
4
提示
There are four Shichi-Go-San numbers not greater than 575: 357,375,537 and 573.
题目大意:给你一个整数N,让你找出从1到N的范围内的整数中,出现只有‘3’,‘5’,‘7’组合成的数数字有多少个,并且‘3’,‘5’,‘7’,这三个数字每个数字都至少出现一次。
想想这道题也是相隔了许久才又回来补的题啦,当时一直没弄明白,自从昨晚做了那个种花的题,幡然醒悟,就过来A了它了,这个题其实和上一道题是类似的。思路枚举位数小于等于N的位数+1的所有由‘3’,‘5’,‘7’组成的整数,(这里为什么要N的位数加一呢,这只不过是给上了保险,保证枚举了所有的情况),然后把这些整数用数组记录下来,具体有多少个写代码的时候我也没进行计算,直接就把数组开了1e6的空间,写出来后,我输入n=1e9进行测试,得到的结果是26484,一开始数组开大一点也是为了保险,以防数据溢出,还有一点就是这个数组的类型需要开long long 类型的,因为枚举的是N的位数+1的所有情况,所以会爆int,(亲测有效,不这样操作的话,会WA )
上代码:
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
int k,n;
char num[15];
bool chack(char a[])
{
int flag1 = 0, flag2 = 0, flag3 = 0;
for (int i = 1;i <= k;i++)
{
if (a[i] == '3') flag1 = 1;
if (a[i] == '5') flag2 = 1;
if (a[i] == '7') flag3 = 1;
}
if (flag1 && flag2 && flag3) return true;
else return false;
}
ll qpow(int x, int y)
{
ll res = 1;
for (int i = 0;i < y;i++)
{
res *= 1ll * x;
}
return res;
}
ll st[1000010],s;
char b[4] = { '\0','3','5','7' };
void dfs(int t)
{
if (t > k)
{
if (chack(num))
{
s++;
for (int i = 1,j=k-1;i <= k;i++,j--)
{
st[s] += (1ll*num[i] - '0') * qpow(10, j);
}
}
return;
}
for (int i = 1;i <= 3;i++)
{
num[t] = b[i];
dfs(t + 1);
num[t] = '\0';
}
}
int main()
{
int digt= 0;
cin >> n;
int tmp = n;
while (tmp)
{
digt++;
tmp /= 10;
}
for (k = 3;k <= digt+1;k++)
{
dfs(1);
}
sort(st + 1, st + s + 1);
int ans = 0;
for (int i = 1;i <= s;i++)
{
if (st[i] > 1ll*n)
{
ans = i - 1;
break;
}
}
cout << ans << endl;
return 0;
}
本博客就记录一些列DFS的题吧,之后再遇见相关题的话,回头再来补上。
待续。。。