“逐步生成结果“类问题

"逐步生成结果"类问题之数值类

1.上楼梯

        有一个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶2阶3阶,

请编写程序,计算出小孩有多少种上楼梯的方法

        题目分析,只有一个台阶的时候f(1)=1,当有两个台阶的时候f(2)=2,当有三个台阶的时候f(3)=4,当有四个台阶的时候就会有f(4)=f(1)+f(2)+f(3)=7,这样从一部分慢慢的生成结果

//逐步生成结果类问题-数值型
//上楼梯
//DP=f(x-1)+f(x-2)+f(x-3)
#include<stdio.h>
int slt(int n);
int main(void)
{
	int n;
	scanf("%d",&n);
	printf("%d",slt(n));
	return 0;
 } 
 int slt(int n)
 {
 	if(n==1)
 		return 1;
 	if(n==2)
 		return 2;
 	if(n==3)
 		return 4;
 	return slt(n-1)+slt(n-2)+slt(n-3);
 }

2.机器人走方格

        有X*Y的网络,一个机器人只能走格点且只能向右或向下走,从左上角走到右下角,请设计一个算法,计算出机器人有多少种走法。

        题目分析:机器人只有两个情况要么向下走,要么向右走,从一个网格点到达下一个网格点又会有两种情况。向下图这样一步一步去推导,便可以得出规律,递归的代码写出来很简单,但是整个的推导过程就不容易看出。

 

//逐步生成结果类问题-数值型
//机器人走方格
//DP f(x,y)=f(x-1,y)+f(x,y-1);
#include<stdio.h>
int robot_go(int x,int y);
int main(void)
{
	int x,y;
	scanf("%d %d",&x,&y);
	printf("%d",robot_go(x,y));
	return 0;
 } 
 int robot_go(int x,int y)
 {
 	if(x==1||y==1)
 		return 1;
 	return robot_go(x-1,y)+robot_go(x,y-1);
 }

3.硬币表示

        有1,2,5,10,20,50,100,200不同面值的硬币,设计一种算法计算出这些硬币表示k有多少种。

        题目分析:这个就是选和不选加上选几个的问题,我先选200,或者不选200,其他的交给你们去完成。当我们选了两百又会有两种方式,选100和不选100。不选200也有两种方式,选100和不选100,这样就会有很多种组合方式。在选择200时不光是选和不选还有200选几个,100选几个的问题,理解到这一点代码就好些多了。

//逐步生成结果类问题-数值型
//硬币表示
//输入k,可以1,2,5,10,20,50,100,200表示k的种类
#include<stdio.h>
int select_coins(int arr[],int n,int buf_sub);//参数:硬币种类,相当于输入的k,数组下标 
int main(void)
{
	int k;
	scanf("%d",&k);
	int buf[8]={1,2,5,10,20,50,100,200}; //硬币种类 
	printf("%d",select_coins(buf,k,7));//传入参数 
	return 0;
 }
int select_coins(int arr[],int n,int buf_sub)
{
	int res=0;
	if(buf_sub==0)//只要到达1,那么就只有一种方法 
		return 1;
	for(int i=0;i*arr[buf_sub]<=n;i++)//先依照个数选,比如先选零个200,其他的交给其他人,下一次我选1个两百,剩下的交给其他人去组合 
	{//在其他人进行选择中也是,先选零个其他交个别人,下一次在选1个其他的交给别人 
		int shengyu = shengyu-i*arr[buf_sub];
		res+=select_coins(arr,shengyu,buf_sub--);//这里传入的参数 
	}
	return res;
}

 

"逐步生成结果"类问题之非数字类

1.括号组合

        输出n对括号的全部有效组合,例如2对括号,()(),(())

        题目分析:逐步生成结果类问题需要一步一步推导,如下图,只有三种插入方式,插入左边右边,包起来。相同的就删去不在往下。

 

//逐步生成结果类问题-非字符型
//合法括号
//n对括号的全部组合
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h> 
void kuohao(int n,char arr[][100]);//在这里 char arr[][100]中,第二个方括号内必须是准确的数 
int count = 0;
int main(void)
{
	int n;
	scanf("%d",&n);
	char buf[100][100];
	kuohao(n,buf);
	for(int i=0;i<count;i++)
	{
		printf("%s\n",buf[i]);
	}
	return 0;
 } 
 void kuohao(int n,char arr[][100])
 {
 	if(n==1)
	{
		strcpy(arr[0],"()");
		count++;
		return ;
	}
	kuohao(n-1,arr);
	
	//printf("count:%d\n",count);
	
	int cnt=0;//因为每次添加括号都会使字符串数量改变,变量cnt用于刷新字符串的数量
	
	//printf("cnt:%d\n",cnt);
	
	//先创建数组pz,拷贝arr里面的内容
	char**pz = (char **) malloc(count * sizeof(char *));
	if(pz==NULL)
	{
		printf("内存分配失败\n"); 
		exit(-1);
	}
	for(int i=0;i<count;i++)
	{
		pz[i]=(char *)malloc(strlen(arr[i])*sizeof(char));
		strcpy(pz[i],arr[i]); 
	}
	
	//,拷贝后可将数组初始化
	memset(arr,0,sizeof(arr));
	//printf("11%s11",arr[0]);
	//printf("%s\n",pz[0]);
	//对aaa数组里面的每一个字符串进行,加前括号,加后括号,包字符串
	//期间要判断加前括号和加后括号是否相等、
	for(int i=0;i<count;i++)
	{ 	
		 sprintf(arr[cnt],"%c%s%c",'(',pz[i],')');
		 cnt++;
		 sprintf(arr[cnt],"%s%c%c",pz[i],'(',')');
		 cnt++;
		 sprintf(arr[cnt],"%c%c%s",'(',')',pz[i]);
		 if(strcmp(arr[cnt],arr[cnt-1])==0)
		 	memset(arr[cnt],0,sizeof(arr[cnt]));
		 else
		 	cnt++;
	}
	
	//内存释放 
	for(int i=0;i<count;i++)
	{
		free(pz[i]);
	}
	free(pz);
	count=cnt;
 }

2.非空子集

        返回某集合的所有子集,例如{A,B,C}子集有{A}{B}{C}{A,B}{A,C}{B,C}{A,B,C}

        题目分析:法一:递归的老板思维,对于例子中的字符集,求子集时先让{A,B}把字符集求好,在用C字符去添加或不加

       法二:运用二进制,字符集有3个元素,所组合成的非空子集有2^3-1种,对应的二进制为111,说明二进制1~111种二进制有1的说明该元素能够取到,为0则取不到,这样遍历1~7的二进制对应的1就可以得出子集。

//逐步生成结果类问题-非字符型
//非空子集
//返回某个集合的所有子集 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h> 
void ziji(char arr[][100],char arr_c[],int n);//n表示前几个元素后面的求非空子集 
int cnt=0;//用于字符串计数 
int main(void)
{
	char buf0[]="AB";
	char buf1[100][100];
	ziji(buf1,buf0,strlen(buf0));
	for(int i=0;i<cnt;i++)
	{
		printf("%s\n",buf1[i]);
	}
	return 0;
 } 
 void ziji(char arr[][100],char arr_c[],int n)
 {
 	if(n==1)
 	{
 		sprintf(arr[cnt++],"%c",arr_c[n-1]);
 		return ;
	}
	ziji(arr,arr_c,n-1);
	
	//先动态创建数组,将数组arr[][]里面的内容复制到创建的数组里面
	char **pz=(char **)malloc(cnt*sizeof(char *));
	if(pz==NULL)
	{
		printf("动态分配内存失败\n");
		exit(-1);
	} 
	for(int i=0;i<cnt;i++)
	{
		pz[i]=(char*)malloc(100*sizeof(char));
		strcpy(pz[i],arr[i]);
	}
	
	//然后初始化arr[][],以便后面元素到arr[][]
	memset(arr,0,sizeof(arr));
	
	int count=0;//记录在这个递归过程中,记录arr有多少条字符串 
	
	for(int i=0;i<cnt;i++)
	{
		strcpy(arr[count++],pz[i]);//不选择该元素
		sprintf(arr[count++],"%s%c",pz[i],arr_c[n-1]);//选择该元素 
	}
	sprintf(arr[count++],"%c",arr_c[n-1]);//该元素自己成为独立的一个字符串 
	
	//内存释放 
	for(int i=0;i<cnt;i++)
	{
		free(pz[i]);
	}
	free(pz);
	
	//更新外围即计数变量 
	cnt=count;
 }
//逐步生成结果类问题-非字符型
//非空子集
//返回某个集合的所有子集 
//另一种解法,巧用二进制
#include<stdio.h>
#include<string.h>
#include<math.h>
void get_bin(int num,int arr[]);
int main(void)
{
	char arr_char[]="ABC";
	int arr_bin[strlen(arr_char)];
	int num=pow(2,strlen(arr_char))-1;
	while(num)
	{
		get_bin(num,arr_bin);
		for(int i=0;i<strlen(arr_char);i++)//去判断每一位上是0还是1 
		{
			if(arr_bin[i]==1)
				printf("%c",arr_char[i]);
		}
		printf("\n"); 
		memset(arr_bin,0,sizeof(arr_bin));
		num--;
	}
	return 0;
 } 
 void get_bin(int num,int arr[])//得到二进制数 
 {
 	int i=0;
 	while(num)
 	{
 		arr[i++]=num%2;
 		num=num/2;
	 }
 }

3.全排列

对一个字符串,输出它的所有全排列字符串

题目分析:他不是硬币表示的要不要要几个的问题,它是全都要.

法一:插入的方式,先有一个字符串进行全排列就是它本身,又来了一个字符B,那么把他插入到最前面和最后面就有AB,BA,再来一个字符C就有CAB,ACB,ABC,对字符串AB进行插入还有CBA,BCA,BAC对字符串BA进行插入,这样反复。

法二:前缀法,利用回溯一个一个的把他交换到最前面,如下图逐步生成结果。

//ti7-2-4
//全排列
//插入的方式 
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
void sort_over(char arr[],char rqarr[][100],int n);
bool cheak_str(char arr[],char rqarr[][100],int k);
int count=0;//记录rqarr二维数组中字符串的串数 
int main(void)
{
	char buf[10]="ABC";
	char rqbuf[100][100];
	sort_over(buf,rqbuf,strlen(buf));
	for(int i=0;i<count;i++)
	{
		printf("%s\n",rqbuf[i]);
	 } 
	return 0;
 }
 void sort_over(char arr[],char rqarr[][100],int n)
 {
 	if(n==1)
 	{
 		sprintf(rqarr[count++],"%c",arr[n-1]);
 		return ;
	 }
	 sort_over(arr,rqarr,n-1);
	 
	 //分配内存,创建一个指向二维数组的指针
	 //拷贝一份原始数组的内容 
	 char **pz=(char **)malloc(count*sizeof(char *));
	 if(pz==NULL)
	 {
	 	printf("动态内存分配失败\n");
	 	exit(-1);
	  } 
	  for(int i=0;i<count;i++)
	  {
	  	pz[i]=(char *)malloc(100*sizeof(char));
	  	strcpy(pz[i],rqarr[i]);
	  }
	  
	 //初始化二维字符串数组
	 memset(rqarr,0,sizeof(rqarr)); 

	//记录新的二维字符串里面有多少
	 int cnt=0;
	  
	//对二维字符串的每一条依次插入,i=0;插入第一条 
	 for(int i=0;i<count;i++) 
	 {
	 	for(int j=0;j<=strlen(pz[i]);j++)
		{
			char str0_temp[100];//存放插入位置的前面的字符串 
			char str1_temp[100];//存放插入位置的后面的字符串
			char str_temp[100];//存放插入好的字符串
			/*
			下面的注释代码是哪来避免只有字符串A时,截取字符串前缀和后缀问题
			原因是因为在同一个for循环里,被分到同一块内存,在结束时仍然会保留上一次的结果
			这个问题用下面的三个memset解决了 
			*/
//			if(j==0)
//			{
//				sprintf(str_temp,"%c%s",arr[n-1],pz[i]);
//				//printf("%s\n",str_temp);
//				if(cheak_str(str_temp,rqarr,cnt)==false)//证明没有重复
//				{
//					strcpy(rqarr[cnt++],str_temp);
//				} 
//				continue;
//			}
//			if(j==strlen(pz[i]))
//			{
//				sprintf(str_temp,"%s%c",pz[i],arr[n-1]);
//			//	printf("%s",str_temp);
//				if(cheak_str(str_temp,rqarr,cnt)==false)//证明没有重复
//				{
//					strcpy(rqarr[cnt++],str_temp);
//				} 
//				continue;
//			}
			strncpy(str0_temp,pz[i],j);
			strncpy(str1_temp,pz[i]+j,strlen(pz[i])-j);
			sprintf(str_temp,"%s%c%s",str0_temp,arr[n-1],str1_temp);
			//检测字符串strtemp在rqarr数组中是否有重复 
			if(cheak_str(str_temp,rqarr,cnt)==false)//证明没有重复
			{
				strcpy(rqarr[cnt++],str_temp);
			} 
			memset(str0_temp,0,sizeof(str0_temp)); 
			memset(str1_temp,0,sizeof(str1_temp)); 
			memset(str_temp,0,sizeof(str1_temp)); 
		 } 
	 }	
	 //释放内存 
	for(int i=0;i<count;i++)
	{
	  	free(pz[i]);
	}
	free(pz);
	//更新二维字符串中字符串的数量 
	count=cnt;
  } 
  bool cheak_str(char arr[],char rqarr[][100],int k)
  {
  	for(int i=0;i<k;i++)
  	{
  		if(strcmp(rqarr[i],arr)==0)
  			return true;//证明有相同的字符串 
	  }
	return false;//证明没有相同的字符串 
  }
//ti7-2-5
//全排列
//回溯的方式
#include<stdio.h>
#include<string.h>
void sort_huisu(char arr[][100],char buf[],int k);
void swap(char arr[],int i,int k); 
int count=0;//记录二维数组中字符串的串数 
int main(void)
{
	char arrr[100][100];
	char arr[]="ABCD";
	sort_huisu(arrr,arr,0);
	for(int i=0;i<count;i++)
	{
		printf("%s\n",arrr[i]);
	}
	return 0;
 } 
void sort_huisu(char arr[][100],char buf[],int k)
{
	if(k==strlen(buf))
	{
		strcpy(arr[count++],buf);
		return ;
	}
	for(int i=k;i<strlen(buf);i++)
	{
		swap(buf,i,k);
		sort_huisu(arr,buf,k+1);
		swap(buf,i,k);//回溯 
	}
 } 
 void swap(char arr[],int i,int k)
 {
 	char temp=arr[i];
 	arr[i]=arr[k];
 	arr[k]=temp;
 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值