递归训练:(1.打印沙漏,2.波兰国旗问题)

本文介绍了解决两个经典编程问题的方法:使用递归打印沙漏形状,并通过动态规划解决波兰国旗问题,以最少的交换次数将白旗置于红旗前。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差2;符号数先从大到小顺序递减到1,再从小到大顺序递增;首尾符号数相等。

给定任意N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。

输入格式:

输入在一行给出1个正整数N(≤1000)和一个符号,中间以空格分隔。

输出格式:

首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。

输入样例:

19 *
输出样例:

*****
 ***
  *
 ***
*****
2

【思路】
非常棒的递归题。本题一看就发现,大的沙漏里面有一个小的沙漏,即如果我们能解决小沙漏问题,那么打印大沙漏就是在小沙漏的上面和下面分别打印一行就可以了嘛。这就是递归子问题的性质,所以可以用递归来做,比循环方便多了!

f(大沙漏) = 打印第一行 + f(小沙漏) + 打印最后一行
AC代码:

/*
*****
 ***
  *
 ***
*****
*/
#include<iostream>
using namespace std;

int cost = 0;
int n;

void f(int space, int len, char c)
{
	//递归边界 
	if(len == 1)
	{
		for(int i = 1;i <= space;i++)	
		{
			cout << " ";
		} 
		cout << c << endl;
		cost++;
		return ;
	}
	
	//先打印上边界
	for(int i = 1;i <= space;i++)	//打印相应的空格 
	{
		cout << " ";
	} 
	//内容
	for(int i = 1;i <= len;i++)
	{
		cout << c;
		cost++;
	} 
	cout << endl;
	//递归打印子漏斗 
	f(space + 1, len - 2, c);
	
	//打印下边界 
	for(int i = 1;i <= space;i++)	//打印相应的空格 
	{
		cout << " ";
	} 
	for(int i = 1;i <= len;i++)
	{
		cout << c;
		cost++;
	}
	cout << endl;
}

int main()
{
	char c;
	cin >> n >> c;
	int len = 0;			//表示漏斗的最上边边长
	int temp = (n + 1) / 2;
	int last = 0;
	for(int i = 1;true;i += 2)	
	{
		temp = temp - i;
		if(temp < 0)
			break;
		else
			last = i;
	}
	len = last;				//求出len 
	f(0, len, c);
	cout << n - cost << endl;
	return 0;
}

运行结果:
在这里插入图片描述


【问题描述】
**波兰国旗问题
桌上有 n(1<n<10000) 面小旗,一部分是白旗,一部分是红旗(波兰国旗由白色和红色组成)。唯一允许的操作是交换两面小旗位置。请你设计一个算法,用最少的交换操作将所有的白旗都置于红旗的之前。

输入格式

第一行为一个整数,即 n
第二行为 n 个字符,W 表示白旗,用R 表示红旗

输出格式

最小交换次数

输入样例

6
WRWRRW
输出样例

1

【思路】
有点类似区间dp的分析思路,大区间问题建立在小区间问题的基础上。设f(i, j)表示i–j字符串变成波兰国旗的最小交换次数。
所以根据首尾字符情况推导,状态转移方程就不多说了,都在代码里

/*
6
WRWRRW
*/
#include<iostream>
using namespace std;

const int maxn = 10000 + 5;
char a[maxn];

int f(int i, int j)
{
	//边界
	if(i >= j)
		return 0;
	//else 
	if(a[i] == 'R' && a[j] == 'W')
		return f(i + 1, j - 1) + 1;
	else if(a[i] == 'R' && a[j] == 'R')
		return f(i, j - 1);
	else if(a[i] == 'W' && a[j] == 'W')
		return f(i + 1, j);
	else if(a[i] == 'W' && a[j] == 'R')
		return f(i + 1, j - 1);
} 

int main()
{
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
	}
	cout << f(1, n) << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值