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