其他分享
首页 > 其他分享> > 一周总结11.15~11.21——dfs、杨辉三角形、dp

一周总结11.15~11.21——dfs、杨辉三角形、dp

作者:互联网

递归输出数字三角形

【问题描述】
  输出一个n行的与样例类似的数字三角形,必须使用递归来实现
【输入格式】
  一个正整数数n,表示三角形的行数
【输出格式】
  输出一个与样例类似的n行的数字三角形,同一行每两个数之间用一个空格隔开即可(图中只是为防止题面格式化而用’_'代替空格)
【样例输入】
4
【样例输出】
___1
__2_3
_4_5_6
7_8_9_10
【数据规模和约定】
  n<=20
【解题思路】
  看完题目顺着思路想可能会去思考如何通过n判断输出多少个空格?每一行应该输出多少个数字?此时就会想到,如果能先输出最后一行,那我就不用再数要输多少个空格了。这样就很容易能想到用递归做。
  dfs的思想:一条路走到黑,直到走不了才会选择回头,然后重新选择一条路。
以n=4为例:
在这里插入图片描述
有阴影的格子是不需要输出空格的。
从最后一行开始,每走完一行step退后一步,再继续走。

#include <iostream>
using namespace std;

int n, cnt = 1;

void dfs(int x) {
	if (x == 1) {							//dfs结束的标志
		for (int i = 0; i < n - x; i ++ ) {
			cout << " ";
		}
		cout << cnt ++ << endl;
		return;								//返回上一级的dfs函数
	}
	dfs(x - 1);																
	
	//这两个for循环即能输出一行
	for (int i = 0; i < n - x; i ++ )     //输出前面的空格
		cout<<" ";
	for (int i = 0; i < x; i ++ )		  //输出数,别忘了两个数之间用空格隔开
		cout << cnt++ <<" ";
	
	cout << endl;   					 //回车进入下一行
}

int main() {
	cin >> n;
	dfs(n);
	return 0;
}

蓝桥杯第十二届省赛真题-杨辉三角形

【问题描述】
  下面的图形是著名的杨辉三角形:
在这里插入图片描述
  如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
 1,1,1,1,2,3,1,3,3,1,1,4,6,4……
  给定一个整数N,请你输出数列中第一次出现N是在第几个数?
【输入格式】
  输入一个整数N
【输出格式】
  输出一个整数代表答案
【样例输入】
  6
【样例输出】
  13
【评测用例规模与约定】
  对于20%的评测用例,1 <= N <= 10
  对于所有评测用例,1 <= N <= 10^9

【解题思路】

杨辉三角有两个特点:
  1. 对角线和第一列的值全是1(假设它是一个直角三角形,这样比较好算)
  2. 从第三行开始,每一个值都是它的上一个和上一个的前一个值相加而得,即:a[i][j]=a[i-1][j-1]+a[i-1][j]

求解整数N第一次出现在第几个,只需要在计算杨辉三角数的时候加一个判断和计数器,看一下是否等于N,如果是,就可以输出计数器的值。
  于是我们可以比较轻松的写出代码:

#include <iostream>
using namespace std;

long Tri(int r, int c){					//r和c分别是行和列
	return (c == 1 || c == r)? 1 : Tri(r-1,c-1) + Tri(r-1,c);     
}

int main(){
	int n,a[100][100];		            //这里数组的范围定义太大会出问题,所以先来个100*100的二维数组
	cin >> n;
	int cnt = 1;						//从1开始计数
	for(int i = 1; i <= n; i++){
		for(int j = 1;j <= i; j++){
			a[i][j] = Tri(i,j);
			if(a[i][j] == n){			 //判断是否等于n
				cout << cnt;
				break;          		 //输出第一个满足的即可 
			}
			cnt++;					     //每计算完一个数,计数器+1
		}
	}
	return 0;
}

其中Tri函数写的非常巧妙,出自这篇“博客”。它等效于:

//杨辉三角型代码
for(int i=0; i<n; i++){
	a[i][1] = 1;
	a[i][i] = 1;
}
for(int i=3; i<n; i++){
	for(int j=2; j<i-1; j++){
		a[i][j] = a[i-1][j-1] + a[i-1][j];
	}
}
//这是我之前的代码

但是!这个代码花费的空间实在太多了!!

在蓝桥杯系统上跑会出现“运行超时”,而且只有20分!

尝试将数组换成vector容器也会出现空间不足的报错
这意味着这个思路是不可行的
于是我找到了一个非常巧妙并且值得学习的解法:

找规律 + 二分 - 杨辉三角形 - 第十二届蓝桥杯省赛第一场C++B组

(里面的代码在系统上跑可以跑到完美的分数)

用最少的钞票组成指定的金额

【问题描述】
  已知不同面值的钞票,求如何用最少数量的钞票组成某个金额,求可以使用的最少钞票数量。如果任意数量的已知面值都无法组成该金额,则返回-1。
【输入格式】
  第一行输入一个整数n
  第二行依次输入n个整数,代表钞票的面额
  第三行输入目标金额
【输出格式】
  输出一个整数代表答案
【样例输入】
  5
  1 2 5 7 10
  14
【样例输出】
  2
【解题思路】
  最简单的方法是用贪心算法:每次优先选最大的面值,但是放在这题显然不合理——根据贪心算法14金额可由10、2、2组成,一共需要三张,而实际上,只需要两张7元即可解决问题!贪心只能解决一小部分问题,因此对于这题,我们可以使用dp(动态规划)。

dp的思想:分治,在子问题中选最优,从而推算出当前问题的最优解。
举个例子:走楼梯。走楼梯有两种方法:一次走一层和一次走两层,问你走到第 i 层有多少种走法?
       用dp的思想,要计算到第 i 层就要知道第 i -1 层和第 i - 2 层的走法,因为它们有多少种走法直接确定了第 i 层的走法数,以此类推,要知道第 i - 1 层的走法就必须要知道第 i - 2 层和第 i - 3层的走法…………
       因此可以得知dp动态转移方程为:dp[ i ] = dp[ i - 1 ] + dp [ i - 2 ]

那么如何将dp思想运用到这道题目中呢?
两种情况:
  1、如果目标金额刚好与其中一个面值相等,那么使用最少钞票数就是1(最好的情况)
  2、否则,计算面值能够组成目标金额的所有组合,求使用的最小钞票数。(遍历,求最小:min方法)
  如:14可由10+4组成,而4又可分为2+2,故14的其中一种解法是:10、2、2(例子不代表最佳)
  动态转移方程:dp[ i ] = min( dp[ i - 1] , dp[ i - 2 ] . dp[ i - 5 ] . dp[ i - 7 ] . dp[ i - 10 ] ) + 1
  在这里插入图片描述

其中 dp[ i ]中存储金额1 ~ i 的最优解
coins数组存储钞票面值

int Coinproblem(vector<int>& coins, int amount){
	vector<int> dp;
	for(int i = 1; i <= amount; i++){		//初始化dp数组 
		dp.push_back(-1);
	}
	dp[0] = 0;								//特殊:金额0的最优解为0 
	for(int i = 1; i <= amount; i++){		//求i~amount金额的最优解,并存进dp数组 
		for(int j = 0; j < coin.size(); j++){
			if(i - coins[j] >= 0 && dp[i-coins[j]] != -1){
				if(dp[i] == -1 || dp[i] > dp[i-coins[j]]+1){
					dp[i] = dp[i-coins[j]]+1;
				}
			}
		}
	}
	return dp[amount];
}

两个if条件语句的意思:
1、i - coins[ j ] >= 0 :钞票面值必须小于等于金额
  dp[ i - coins[ j ] ] != -1 :必须有此面值的钞票
2、dp[ i ] == -1:无此面值的钞票
  dp[ i ] > dp [ i - coins[ j ] ] + 1 :判断组合是否比原组合数更小。其实这句结合下面一句dp[ i ] = dp[ i -coins [ j ] ] + 1就类似于min方法,把大的用小的替换掉。

标签:输出,coins,int,11.15,样例,dfs,钞票,杨辉三角,dp
来源: https://blog.csdn.net/ihavea_bug/article/details/121353601