【DFS】关于深搜的系列问题

一:全排列系列

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;
}

------------------------------------------------------分割线------------------------------------------------------------------------


  • 2.用皇后所在列数表示皇后所在位置的n皇后问题

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的题吧,之后再遇见相关题的话,回头再来补上。

待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DFS(Depth-First Search)即深度优先搜索,是一种常用的图遍历算法。在DFS中,我们从图的某个顶点开始,沿着一条路径尽可能深的搜索,直到无法继续为止,然后回溯到前一个节点,再继续搜索其他未访问的节点。 在C++中,我们可以使用递归或者栈来实现DFS算法。下面是一个使用递归实现DFS的示例代码: ```cpp #include <iostream> #include <vector> using namespace std; vector<vector<int>> graph; // 图的邻接表表示 vector<bool> visited; // 记录节点的访问状态 // 深度优先搜索函数 void dfs(int node) { visited[node] = true; // 标记当前节点为已访问 // 对于当前节点的所有未访问的邻居节点,递归调用dfs函数 for (int neighbor : graph[node]) { if (!visited[neighbor]) { dfs(neighbor); } } } int main() { int numNodes, numEdges; cin >> numNodes >> numEdges; // 初始化图的邻接表和访问状态数组 graph.resize(numNodes); visited.resize(numNodes, false); for (int i = 0; i < numEdges; i++) { int u, v; cin >> u >> v; graph[u].push_back(v); graph[v].push_back(u); // 若为有向图则可省略这行 } int startNode; // 指定DFS的起始节点 cin >> startNode; dfs(startNode); // 输出所有已访问的节点 for (int i = 0; i < numNodes; i++) { if (visited[i]) { cout << i << " "; } } cout << endl; return 0; } ``` 在上述代码中,我们通过邻接表来表示图,并使用visited数组记录节点的访问状态。dfs函数按照深度优先的方式遍历图,递归调用自身来访问当前节点的邻居节点。最后,我们输出所有已访问的节点。 需要注意的是,DFS算法对于有环的图需要进行环的检测,以避免无限循环。另外,如果图是一个森林(由多个连通分量组成),我们需要对每个连通分量都执行DFS算法。 希望这个示例能帮助你理解DFS在C++中的实现。如果有任何疑问,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值