走方格(滚动数组优化dp、排列组合、递归爆搜)

走方格(滚动数组优化dp、排列组合、递归爆搜)

传送门

滚动数组优化

离谱的过程

只要根据dp数组的存储规律和转移方程来就好,在二维dp的代码基础上稍加改动更是简单。

一来根本不用搞劳什子对dp数组的初始化(顺了题意)
二来,在为dp数组元素求值的时候,也就是按照转移方程的规律来呀,根本没有什么固定的滚动数组滚动方式(优化方法)
而我偏偏铁了心的想套用0-1背包的滚动数组优化dp,就是 从上往下求求每一行的,对于每一行中的元素采用从右到左的求法,晕倒。。。0-1背包的转移方程跟这个无甚关系,怎能混为一谈。 dp数组的元素求值记录过程中(前者要用到 所求元素 的 上头元素 和 左上角一个元素,要用到的两个元素都在上一行中,由于从上往下的顺序,求下一行的任一元素可以以任意顺序,既可从左到右,又可从右到左,为啥?因为不需要用到同一行的元素呀,要用到的元素在上一行之前就已经求出来了)(而后者要用到 所求元素 的上头元素(在上一行)和 位于同一行的前一个元素,从上往下求上一行已经求出,即上头元素可以得到,但是欲求这个元素,必先求出 同一行的前一个元素,这可不必须从左往右吗,从右往左不能说是南辕北辙,只能说是毫无干系,离谱
0-1背包滚动数组优化

 #include<bits/stdc++.h>
using namespace std;

int main(){
	int m,n;
	cin>>m>>n;
	int dp[n+1];
//	for(int i=1;i<=n;i++)dp[i]=0;
//	dp[0]=0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(i==1||j==1)dp[j]=1;
			//只能向右或向下,所以但凡 i==1||j==1只有一种走法 
			else  dp[j]=dp[j]+dp[j-1];
		}
	}
//	for(int i=1;i<=m;i++){
//		for(int j=n;j>=1;j--){
//			if(i==1||j==1)dp[j]=1;
//			//只能向右或向下,所以但凡 i==1||j==1只有一种走法 
//			else  dp[j]=dp[j]+dp[j-1];
//		}
//	}//2 3
	cout<<dp[n]<<endl;
    return 0;
} 

完整的perfect结果

#include<bits/stdc++.h>
using namespace std;
void dp_1(int m,int n){
	int dp[m+1][n+1];
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(i==1||j==1)dp[i][j]=1;
//不用初始化,一来从题意上,第一行和第一列的元素到达方式都只有一种
//二来dp数组的求值用不到第0行、列的元素 
			else dp[i][j]=dp[i-1][j]+dp[i][j-1];
		}
	}
	cout<<dp[m][n]<<endl; 
}
void dp_2(int m,int n){
	int pre[n+1];//专门用来记录一行,再用一个数组记录下一行再进行更新覆盖 
	int cur[n+1];
	memset(cur,0,sizeof(cur));
	//pre数组表示已经求完毕的 可用于求下一行的 上一行的情况
	//cur数组是正在求的一行,求完毕之后要更新pre数组用于求接下来的一行 
	for(int i=1;i<=n;i++){//初始化了第一行的情况 
			pre[i]=1; 
	}
	for(int i=2;i<=m;i++){
		for(int j=1;j<=n;j++){
			cur[j]=cur[j-1]+pre[j]; 
		}
		for(int j=1;j<=n;j++){
			pre[j]=cur[j]; 
		}
	}
	cout<<pre[n]<<endl;
} 
void dp_3(int m,int n){
	int dp[n+1];
//由于求一个元素只需要用到上一行的和同行前一个元素,
//通过不断更新这一行来存储 
	 for(int i=1;i<=m;i++){
	 	for(int j=1;j<=n;j++){
	 		if(i==1||j==1)dp[j]=1;
	 		else dp[j]=dp[j-1]+dp[j];
	//分别代表同行前一个元素和上一行这列的元素 
		 }
	 } 
	 cout<<dp[n]<<endl;
}
int main(){
	int m,n;
	cin>>m>>n;
	dp_1(m,n);
	dp_2(m,n);
	dp_3(m,n);
    return 0;
} 

排列组合

 #include<bits/stdc++.h>
using namespace std;

int main(){
	int m,n;
	cin>>m>>n;
	int a=m-1;
	int b=n-1;
//	if(a>b)swap(a,b);没必要 
	long long int res=1;
	int j=2;
	for(int i=a+b;i>a;i--){// i>a,连乘到a+1就好 
//排列组合求Cm+n、n=!(m+n)/(!n)*(!m) 
//求!(m+n)/(!n),那么就是m+n——n+1这些数连乘 
		res*=i;
		if(j<=b&&res%j==0){//边乘边除,防止溢出,也可以等除的进再除 
			res/=j;
			j++;
		}
	} 
	 cout<<res<<endl;
    return 0;
} 

递归

#include<bits/stdc++.h>
using namespace std;
int dp(int i,int j){
	if(i==1||j==1)return 1;
	return dp(i-1,j)+dp(i,j-1);
}
int main(){
	int m,n;
	cin>>m>>n;
	cout<<dp(m,n);
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值