DFS(深度优先搜索)做题记录

P1030 [NOIP2001 普及组] 求先序排列

题目描述
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度 ≤8)。

输入格式
2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。

输出格式
1行,表示一棵二叉树的先序。

样例
输入
BADC
BDCA
输出
ABCD

思路:
中序:左-根-右
后序:左-右-根
可以凭后序从后往前找根,找到一个根就输出,然后到中序里面分出左串和右串然后一直递归重复直到找完。

ps:dfs用的还是挺懵的,这题看一楼大佬的代码写的orz

//substr()函数作用是拷贝字符串

#include<iostream>
#include<string>
using namespace std ;

void dfs( string in , string after )
{
	if( in.length() > 0 )
	{
		char ch = after[after.length() - 1] ;
		cout << ch ;
		int k = in.find(ch) ;
		dfs( in.substr( 0 , k ) , after.substr( 0 , k ) ) ;
		dfs( in.substr( k + 1 ) , after.substr( k , in.length() - k - 1 )) ;
	}
}

int main()
{
	string in , af ;
	cin >> in >> af ;
	dfs( in , af ) ;
	cout << endl ;
	return 0 ;
}

UVA536 求后序排列

题目:
Little Valentine liked playing with binary trees very much. Her favorite game was constructing randomly looking binary trees with capital letters in the nodes.
This is an example of one of her creations:

                                           D

                                          / \

                                         /   \

                                        B     E

                                       / \     \

                                      /   \     \

                                     A     C     G

                                                /

                                               /

                                              F

To record her trees for future generations, she wrote down two strings for each tree: a preorder traversal (root, left subtree, right subtree) and an inorder traversal (left subtree, root, right subtree). For the tree drawn above the preorder traversal is DBACEGF and the inorder traversal is ABCDEFG.
She thought that such a pair of strings would give enough information to reconstruct the tree later (but she never tried it).

Now, years later, looking again at the strings, she realized that reconstructing the trees was indeed possible, but only because she never had used the same letter twice in the same tree.
However, doing the reconstruction by hand, soon turned out to be tedious.
So now she asks you to write a program that does the job for her!

Input
The input will contain one or more test cases.
Each test case consists of one line containing two strings preord and inord, representing the preorder traversal and inorder traversal of a binary tree. Both strings consist of unique capital letters. (Thus they are not longer than 26 characters.)
Input is terminated by end of file.

Output
For each test case, recover Valentine’s binary tree and print one line containing the tree’s postorder traversal (left subtree, right subtree, root).

Sample Input
DBACEGF ABCDEFG
BCAD CBAD
Sample Output
ACBFGED
CDAB

题意:
给出前序和中序求后序排列。

#include<iostream>
#include<string>
using namespace std;

string a, b;

void dfs(int x, int y, int r, int u)
{
	if(x > y)
		return ;
	int k = r;
	while(b[k] != a[x])
		k++;
	int e = k - r;	//左子树长度 
	dfs(x+1, x+e, r, r+e-1);	//-1是减去根结点 
	dfs(x+e+1, y, k+1, u);
	cout << a[x] ;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	while(cin >> a >> b)
	{
		int len = a.length();
		dfs(0, len-1, 0, len - 1);
		cout << endl;
	}
	return 0;
}

P1036 [NOIP2002 普及组] 选数

题目描述
已知 n 个整数 x1,x2,…,xn,以及1个整数k(k<nk<n)。从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n=4,k=3,4个整数分别为3,7,12,19时,可得全部的组合与它们的和为:
3+7+12=22
3+7+19=29
7+12+19=38
3+12+19=34
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29。

输入格式
键盘输入,格式为:

n,k (1 ≤ n ≤ 20 , k < n )
x1,x2,…,xn ( 1 ≤ xi ≤ 5000000 )

输出格式
屏幕输出,格式为: 11个整数(满足条件的种数)。

样例
输入
4 3
3 7 12 19
输出
1

思路:
素数筛 + dfs ,这题用普通的判断素数也ok,有一点需要注意的是用dfs时的那个for循环,不降原则去重且不能平。

#include<iostream>
using namespace std ;

const int N = 5e6 + 5 ;
const int maxn = 1e6 + 5 ; 
long prime[maxn] , countnum = 0 ;
bool p[N] = { false } ;

long n , k , a[20] , sum = 0 ;

void isprime()
{
	long i , j ;
	for( i = 2 ; i < N ; ++i )
	{
		if( p[i] == false )
			prime[countnum++] = i ;
		for( j = 0 ; j < countnum ; ++j )
		{
			if( i * prime[j] > N )
				break ;
			p[ i * prime[j] ] = true ;
			if( i % prime[j] == 0 )
				break ;
		}
	}
}

void dfs( int x , int y , int sso )
{
	if( y == k )
	{
		if( !p[x] )
			sum++ ;
		return ;
	}
	else
	{
		for( int i = sso ; i < n ; ++i )
		{
			dfs( x + a[i] , y + 1 , i + 1 ) ;
		}
		return ;
	}
}

int main()
{
	ios::sync_with_stdio(false) ;
	cin.tie(0) ;
	cout.tie(0) ;
	
	isprime() ;
	cin >> n >> k ;
	for( int i = 0 ; i < n ; ++i )
		cin >> a[i] ;
	dfs( 0 , 0 , 0 ) ;
	cout << sum << endl ;
	return 0 ;
}

P1037 [NOIP2002 普及组] 产生数

题目描述
给出一个整数 n(n ≤ 10^30 )和 k 个变换规则(k ≤ 15 )。

规则:
一位数可变换成另一个一位数。
规则的右部不能为零。
例如:n=234。有规则(k=2):
22->55
33->66
上面的整数 234 经过变换后可能产生出的整数为(包括原数):
234
534
264
564
共 4 种不同的产生数。
现在给出一个整数 n 和 k 个规则。求出经过任意次的变换(0次或多次),能产生出多少个不同整数。
仅要求输出个数。

输入格式
第一行两个整数 n,k。
接下来 k 行,每行两个整数 xi,yi 。

输出格式
输出能生成的数字个数。

样例
输入
234 2
2 5
3 6
输出
4

思路:
dfs + 高精度乘法
(因为n是10的30次方,易知最后输出的种数很容易爆long long ,所以要用高精度乘法模拟,这里用的是高精度乘低精度)
注意有两种转换直接转换和间接转换

ps:这题自己用dfs写的时候感觉很对,然后wa了一片,最后看了三楼大佬的代码,终于悟了QAQ

#include<iostream>
#include<memory.h>
#include<string>
using namespace std ;

string s ;
int d[35][35] , w[35] , m , num[35] , a[35] ;
bool vis[15] ;

void dfs( int x , int y )
{
	if( vis[y] )
		return ;
	vis[y] = 1 ;
	m++ ;
	//搜索间接转换 
	for( int i = 1 ; i <= num[y] ; ++i )
		dfs( y , d[y][i] ) ;
}

void mul()
{
	int jw = 0 ;
	for( int i = 30 ; i ; --i )
	{
		a[i] = a[i] * m + jw ;
		jw = a[i] / 10 ;
		a[i] %= 10 ;
	}
}

int main()
{
	ios::sync_with_stdio(false) ;
	cin.tie(0) ;
	cout.tie(0) ;
	
	a[30] = 1 ;
	cin >> s ;
	int k ;
	cin >> k ;
	for( int i = 0 ; i < k ; ++i )
	{
		int x1 , x2 ;
		cin >> x1 >> x2 ;
		d[x1][++num[x1]] = x2 ;
	}
	for( int i = 0 ; i < s.length() ; ++i )
	{
		w[i+1] = s[i]-'0' ; 
	}
	for( int i = 1 ; i <= s.length() ; ++i )
	{
		m = 1 ; 
		memset( vis , 0 , sizeof(vis) ) ;
		vis[w[i]] = 1 ; 
		//搜索直接转换 
		for( int j = 1 ; j <= num[w[i]] ; ++j )
			dfs( w[i] , d[w[i]][j] ) ;
		mul() ;
	}
	int u = 0 ;
	for( ; a[u] == 0 ; ++u ) ; //去除前导零 
	for( int i = u ; i <= 30 ; ++i )
		cout << a[i] ;
	cout << endl ;
	return 0 ;
 } 

P1460 [USACO2.1]健康的荷斯坦奶牛 Healthy Holsteins

题目描述
农民 John 以拥有世界上最健康的奶牛为傲。他知道每种饲料中所包含的牛所需的最低的维他命量是多少。请你帮助农夫喂养他的牛,以保持它们的健康,使喂给牛的饲料的种数最少。

给出牛所需的最低的维他命量,输出喂给牛需要哪些种类的饲料,且所需的饲料剂量最少。

维他命量以整数表示,每种饲料最多只能对牛使用一次,数据保证存在解。

输入格式
第一行一个整数 v,表示需要的维他命的种类数。
第二行 v 个整数,表示牛每天需要的每种维他命的最小量。

第三行一个整数 g,表示可用来喂牛的饲料的种数。
下面 g 行,第 n 行表示编号为 n 饲料包含的各种维他命的量的多少。

输出格式
输出文件只有一行,包括牛必需的最小的饲料种数 p;后面有 p 个数,表示所选择的饲料编号(按从小到大排列)。

如果有多个解,输出饲料序号最小的(即字典序最小)。

样例
输入
4
100 200 300 400
3
50 50 50 50
200 300 200 300
900 150 389 399
输出
2 1 3

说明/提示
【数据范围】
对于 100% 的数据,1≤v≤25,1≤v≤25,1≤g≤15,1≤g≤15。
输入的所有整数在 [1,1000] 范围内。

思路:
dfs搜索+回溯
这题的存储数据方式是用一个数组放要拿的牛饲料编号,然后分别去求各维生素的总和来判断。

ps:这题不难,问题主要是回溯方面还是比较模糊,其次把存储数据弄得很麻烦。

#include<iostream>
using namespace std;

#define ll long long
const int N = 30;
int a[N];	//存储牛饲料
int b[N][N];	//存储各个维生素
int c[N];	//存储每次要拿的那个牛饲料的编号
int ans[N]; //存储最终解
int n, m;
int mi = 0x3f3f3f3f;	//存储最小使用种数

int check(int x)
{
	for (int i = 1; i <= n; ++i)
	{
		int sum = 0; //记录每种维生素的总和	
		for (int j = 1; j <= c[x]; ++j)
		{
			sum += b[c[j]][i];
			//c[x]存储的是各个编号,这里相当于对同一种需要的维生素进行相加
		}
		//如果小于,就可以结束了,表示不满足
		if (sum < a[i])
			return 0;
	}
	return 1;
}

inline void dfs(int t, int s)
{
	//t代表当前是哪一种牛饲料,s代表当前已经拿了多少种
	if (t > m)
	{
		//超出了牛饲料范围,搜索完了
		if (check(s))
		{
			//判断是否满足牛维生素条件
			if (s < mi)
			{
				//取种数更小
				mi = s;
				for (int i = 1; i <= s; ++i)
					ans[i] = c[i];
				//更新答案编号
			}
		}
		return;
	}
	//回溯
	//两种情况——取or不取
	
	//取,存下当前编号
	c[s + 1] = t;
	dfs(t + 1, s + 1);
	//不取,因为上面取了,所以要把它重新变回
	c[s + 1] = 0;
	dfs(t + 1, s);
}

int main()
{
	cin >> n;
	for (int i = 1; i <= n; ++i)
		cin >> a[i];
	cin >> m;
	for (int i = 1; i <= m; ++i)
		for (int j = 1; j <= n; ++j)
			cin >> b[i][j];

	dfs(1, 0);
	cout << mi;
	for (int i = 1; i <= mi; ++i)
		cout << " " << ans[i];

	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值