下面按照你的要求来讲:先给完整代码,再讲解题思路,再解释为什么要用到这些语法、数组、循环,最后逐行解释代码。
#include <iostream>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int dp[10][10];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (i == 0 || j == 0) {
dp[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
}
cout << dp[n][m] << endl;
return 0;
} 题目给你一个n × m的方格。
你从左上角出发,要走到右下角。
每一步只能:
向右走 或者 向下走
问你一共有多少种不同走法。
例如输入:
2 2
表示有一个2 × 2的方格。
从左上角走到右下角,需要走:
2 次向下 2 次向右
所有走法有:
右 右 下 下 右 下 右 下 右 下 下 右 下 右 右 下 下 右 下 右 下 下 右 右
一共6种,所以输出:
6
这道题可以用 动态规划 来做。
动态规划可以简单理解为:
把大问题拆成小问题,先算出小位置的走法数量,再推出后面位置的走法数量。
我们定义:
dp[i][j]
表示:
从左上角走到第 i 行、第 j 列这个格点,一共有多少种走法。
比如:
dp[2][2]
就表示:
从左上角走到第 2 行、第 2 列这个位置的走法数量。
因为只能向右或向下走,所以到达某个位置只能从两个方向来:
1. 从上面走下来 2. 从左边走过来
也就是说:
dp[i][j]
只能由:
dp[i - 1][j]
和:
dp[i][j - 1]
推出。
所以公式是:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
意思是:
到达当前位置的走法数量 = 到达上面位置的走法数量 + 到达左边位置的走法数量
代码中有:
int dp[10][10];
这是一个二维数组。
二维数组可以理解成一个表格。
因为这道题是走方格,需要记录每个位置的走法数量。
例如:
dp[0][0] dp[0][1] dp[0][2] dp[1][0] dp[1][1] dp[1][2] dp[2][0] dp[2][1] dp[2][2]
每一个dp[i][j]都表示走到这个位置的方法数。
题目范围是:
1 <= n, m <= 8
但是我们要用到下标:
0 到 n 0 到 m
最大就是:
0 到 8
所以数组至少需要能访问:
dp[8][8]
数组开成:
int dp[10][10];
足够使用。
为什么不是dp[8][8]?
因为 C++ 数组下标从0开始。
如果写:
int dp[8][8];
它的合法下标是:
0 到 7
不能访问dp[8][8]。
所以这里开大一点:
int dp[10][10];
更安全,也更容易理解。
代码中有:
if (i == 0 || j == 0) {
dp[i][j] = 1;
} 这是初始化边界。
第一行表示:
i == 0
也就是最上面一行。
在最上面一行,只能一直向右走。
例如走到:
dp[0][3]
只能这样走:
右 右 右
没有其他选择,所以只有1种走法。
因此:
dp[0][0] = 1 dp[0][1] = 1 dp[0][2] = 1 dp[0][3] = 1
第一列表示:
j == 0
也就是最左边一列。
在最左边一列,只能一直向下走。
例如走到:
dp[3][0]
只能这样走:
下 下 下
没有其他选择,所以也是1种走法。
因此:
dp[0][0] = 1 dp[1][0] = 1 dp[2][0] = 1 dp[3][0] = 1
代码中有:
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
...
}
} 因为dp是二维数组,相当于一个表格。
表格有行和列。
外层循环控制行:
for (int i = 0; i <= n; i++)
内层循环控制列:
for (int j = 0; j <= m; j++)
也就是说,这两个循环会把每一个格点都算一遍。
例如n = 2, m = 2时,会计算:
dp[0][0] dp[0][1] dp[0][2] dp[1][0] dp[1][1] dp[1][2] dp[2][0] dp[2][1] dp[2][2]
最后答案就是:
dp[2][2]
也就是:
dp[n][m]
代码中写的是:
for (int i = 0; i <= n; i++)
而不是:
for (int i = 0; i < n; i++)
原因是题目中的n × m个方格,对应的格点是:
从 0 到 n 从 0 到 m
比如2 × 2的方格,格点不是只有:
0, 1
而是:
0, 1, 2
也就是说,需要算到:
dp[2][2]
所以循环必须写:
i <= n j <= m
这样才能包含终点位置。
#include <iostream>
这是引入输入输出库。
因为代码中用到了:
cin cout endl
这些都属于 C++ 的输入输出功能。
它们定义在<iostream>这个头文件中。
如果不写:
#include <iostream>
编译器就可能不认识cin和cout。
using namespace std;
C++ 标准库中的很多东西都放在std这个命名空间中。
例如完整写法应该是:
std::cin std::cout std::endl
写了:
using namespace std;
之后,就可以简写成:
cin cout endl
这样代码更短,更适合初学者。
int main() { 这是程序的主函数。
C++ 程序运行时,会从main函数开始执行。
int表示这个函数最后会返回一个整数。
最后的:
return 0;
就是返回整数0,表示程序正常结束。
cin >> n >> m;
cin是输入对象。
>>是输入运算符。
这句代码表示从输入中读取两个整数,分别放进变量n和m。
例如输入:
2 2
执行后:
n = 2; m = 2;
也可以理解成:
cin >> n; cin >> m;
只不过写在一行更加简洁。
cout << dp[n][m] << endl;
cout是输出对象。
<<是输出运算符。
dp[n][m]是最终答案。
endl表示换行。
例如:
dp[n][m] = 6;
那么输出就是:
6
代码中有:
if (i == 0 || j == 0)
||表示“或者”。
这句话的意思是:
如果 i 等于 0,或者 j 等于 0
只要其中一个条件成立,整体就成立。
也就是说:
在第一行,或者在第一列
都要设置为1。
代码中有:
i == 0 j == 0
==表示判断是否相等。
它和=不一样。
i == 0
表示判断i是否等于0。
i = 0
表示把0赋值给i。
初学者要特别注意:
判断相等用 == 赋值用 =
dp[i - 1][j]
表示当前位置上面的格点。
因为行号减少1,列号不变。
dp[i][j - 1]
表示当前位置左边的格点。
因为行号不变,列号减少1。
例如当前位置是:
dp[2][3]
那么它上面的位置是:
dp[1][3]
它左边的位置是:
dp[2][2]
所以:
dp[2][3] = dp[1][3] + dp[2][2];
#include <iostream>
引入输入输出库。
因为程序需要从键盘读入n和m,还需要输出答案。
所以需要使用:
cin cout endl
这些功能都来自<iostream>。
using namespace std;
使用标准命名空间。
写了这一行后,可以直接使用:
cin cout endl
不用写成:
std::cin std::cout std::endl
int main() { 定义主函数。
程序从这里开始执行。
int n, m;
定义两个整数变量:
n 表示方格的行数 m 表示方格的列数
例如输入:
2 2
那么:
n = 2; m = 2;
cin >> n >> m;
从输入中读取两个整数,分别存入n和m。
比如输入:
2 2
执行后:
n 保存 2 m 保存 2
int dp[10][10];
定义一个二维数组dp。
它用来保存每个位置的走法数量。
其中:
dp[i][j]
表示:
从左上角走到第 i 行、第 j 列,一共有多少种走法。
数组大小写10 × 10,是因为题目中n和m最大是8,我们访问到dp[8][8]就够了,开10更安全。
for (int i = 0; i <= n; i++) { 外层循环,控制行号i。
从第0行开始,一直循环到第n行。
为什么是<= n?
因为终点是:
dp[n][m]
所以必须把第n行也算进去。
for (int j = 0; j <= m; j++) { 内层循环,控制列号j。
从第0列开始,一直循环到第m列。
和行号一样,终点是:
dp[n][m]
所以第m列也必须计算。
if (i == 0 || j == 0) { 判断当前位置是否在第一行或者第一列。
i == 0
表示第一行。
j == 0
表示第一列。
||
表示“或者”。
也就是说:
如果当前位置在第一行,或者当前位置在第一列
就进入这个if。
dp[i][j] = 1;
如果当前位置在第一行或第一列,那么走到这里的方法数是1。
原因是:
第一行只能一直向右走 第一列只能一直向下走
所以没有其他路线。
} else { 如果当前位置不在第一行,也不在第一列,就进入else。
这种位置可以从上面来,也可以从左边来。
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
这是动态规划的核心公式。
当前位置的走法数量等于:
上面位置的走法数量 + 左边位置的走法数量
对应代码就是:
dp[i - 1][j]
表示上面位置。
dp[i][j - 1]
表示左边位置。
所以:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
例如计算:
dp[1][1]
它可以从:
dp[0][1]
走下来,也可以从:
dp[1][0]
走过来。
所以:
dp[1][1] = dp[0][1] + dp[1][0];
因为:
dp[0][1] = 1; dp[1][0] = 1;
所以:
dp[1][1] = 2;
cout << dp[n][m] << endl;
输出最终答案。
因为:
dp[n][m]
表示从左上角走到右下角的方案数。
所以直接输出它。
例如:
dp[2][2] = 6;
输出就是:
6
return 0;
表示程序正常结束。
这是 C++ 程序常见写法。
输入:
2 2
表示要计算:
dp[2][2]
初始化和计算过程如下。
第一行和第一列都是1:
dp[0][0] = 1 dp[0][1] = 1 dp[0][2] = 1 dp[1][0] = 1 dp[2][0] = 1
然后计算其他位置。
dp[1][1] = dp[0][1] + dp[1][0];
也就是:
dp[1][1] = 1 + 1 = 2
dp[1][2] = dp[0][2] + dp[1][1];
也就是:
dp[1][2] = 1 + 2 = 3
dp[2][1] = dp[1][1] + dp[2][0];
也就是:
dp[2][1] = 2 + 1 = 3
dp[2][2] = dp[1][2] + dp[2][1];
也就是:
dp[2][2] = 3 + 3 = 6
最后表格是:
1 1 1 1 2 3 1 3 6
所以输出:
6
因为走到一个位置的方法数,依赖于它前面的两个位置:
上面的位置 左边的位置
而这些位置的方法数可以提前算出来。
也就是说:
小问题:走到前面位置有多少种方法 大问题:走到当前位置有多少种方法
通过小问题推出大问题,这就是动态规划。
这道题的状态转移公式是:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
边界条件是:
dp[i][0] = 1; dp[0][j] = 1;
最终答案是:
dp[n][m];
这份代码主要用到了:
1. cin 输入 2. cout 输出 3. 二维数组 4. for 循环 5. if else 判断 6. 动态规划思想 7. || 或者运算符 8. == 判断相等
其中最关键的是:
int dp[10][10];
用来保存每个位置的走法数。
以及:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
用来根据上面和左边的位置推出当前位置的走法数。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static int findWays(int m,int n){
int[] rec=new int[n];
rec[0]=1;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(j==0) rec[j]=rec[j];
else if(i==0) rec[j]=rec[j-1];
else rec[j]=rec[j-1]+rec[j];
}
}
return rec[n-1];
}
public static void main(String[] args)throws IOException {
//读入和输出的方式要注意,使用bufferedReader中的readLine可以很好知道是否到了末尾
BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=bReader.readLine())!=null) {
int n=Integer.valueOf(line.substring(0,line.indexOf(" ")));
int m=Integer.valueOf(line.substring(line.indexOf(" ")+1));
System.out.println(findWays(n+1, m+1));
}
}
} get_str = input().split(' ')
n, m = int(get_str[0]), int(get_str[1])
# (n+1)*(m+1)的二维dp数组
dp = [[0 for _ in range(m+1)] for _ in range(n+1)]
for i in range(n+1):
for j in range(m+1):
# dp数组初始化
# 原点时0种走法
if i==0 and j==0:
dp[i][j] = 0
# 从原点向下走一步共1种走法
# elif i==0 and j==1:
# 这里可以考虑优化?当退到左边界或上边界时仅剩一种走法
elif i==0:
dp[i][j] = 1
# 从原点向右走一步共1种走法
# elif i==1 and j==0:
elif j==0:
dp[i][j] = 1
# [i][j]点的走法=左边一个点走法+上面一个点的走法
# 即:dp[i][j] = dp[i-1][j] + dp[i][j-1]
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
print(dp[n][m]) import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int n = sc.nextInt();
int m = sc.nextInt();
int countUp = n;
int countDown = n+m;
int num = n;
int zzz = n+m;
// for (int a=n;a>1;--a){
// countUp = countUp*(a-1);
// countDown = countDown*(--countDown);
// }
while (num>1){
countUp = countUp*(num-1);
countDown = countDown*(zzz-1);
num--;
zzz--;
}
int count = countDown/countUp;
System.out.println(count);
}
}
} while True: try: n,m = map(int, input().split()) dp = [[1 for _ in range(m+1)] for _ in range(n+1)] for i in range(1,n+1): for j in range(1,m+1): temp = dp[i-1][j-1]*2 temp1 = 0 temp2 = 0 for k in range(i-1): temp1 += dp[k][j-1] for l in range(j-1): temp2 += dp[i-1][l] dp[i][j] = temp+temp1+temp2 print(dp[n][m]) except: break
#include <bits/stdc++.h>
using namespace std;
int count(int n, int m){
if(n==0||m==0)
return 1;
return count(n-1,m)+count(n,m-1);
}
int main(){
int n,m;
while(cin>>n>>m){
cout<<count(n,m)<<endl;
}
return 0;
} import sys
def f(n,m):
if min(n,m)==1:
return 1 + max(n,m)
else:
return f(n,m-1) + f(n-1,m)
for s in sys.stdin:
s = s.strip().split(" ")
r = f(int(s[0]), int(s[1]))
print(r) #include <iostream>
#include <vector>
using namespace std;
int main(){
int m, n;
while(cin >> m >> n){
vector<vector<int>> F(m + 1, vector<int>(n + 1, 1));
for(int i = 1; i < m + 1; ++i){
for(int j = 1; j < n + 1; ++j){
F[i][j] = F[i - 1][j] + F[i][j - 1];
}
}
cout << F[m][n] << endl;
}
return 0;
}
#include<iostream>
(720)#include<vector>
using namespace std;
int main()
{
int dfs(int n,int m);
int n,m;
while(cin>>n>>m)//n是列,m是行
{
vector<vector<int>>dp(m+1,vector<int>(n+1,1));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
cout<<dp[m][n]<<endl;
//cout<<dfs(n,m)<<endl;
}
}
int dfs(int n,int m)
{
if(m==0&&n==0)
return 1;
if(m>=0&&n>=0)
{
return dfs(n-1,m)+dfs(n,m-1);
}
return 0;
} 里面两种解法,一个dp,一个dfs
#include <stdio.h>
(737)#include <string.h>
int n,m,sum,chess[128][128];
void DFS(int x,int y) //深度优先搜索
{
chess[x][y] = 1;
if(x==m && y==n)
{
sum++;
chess[x][y]=0;
return;
}
if(chess[x+1][y]==0 && x+1<=m)
DFS(x+1,y);
if(chess[x][y+1]==0 && y+1<=n)
DFS(x,y+1);
chess[x][y]=0;
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF)
{
DFS(0,0);
printf("%d\n",sum);
sum =0;
memset(chess,0,sizeof(chess));
}
return 0;
} import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNextInt()){
int m=sc.nextInt();
int n=sc.nextInt();
int t=m+n;
int s=m>n?n:m;
System.out.println(cal(t,s));
}
}
public static int cal(int i,int j){
if(j==0) return i;
int a=1,b=1;
for(int z=0;z<j;z++){
a*=i-z;
b*=z+1;
}
return a/b;
}
} //可以用递归解决,但是最后的函数出口应该是n = 1或者m = 1的情况//另外可以用动态规划方法解决(如下) #include<iostream> using namespace std; int main(){ int n = 0, m = 0; while(cin>>n, cin>>m){ int dp[n+1][m+1]; for(int i =0;i<n+1;i++){ for(int j = 0;j<m+1;j++){ if(i == 0 && j == 0){ dp[i][j]= 1; }else if(i == 0 && j != 0){ dp[i][j] = dp[i][j-1]; }else if(i != 0 && j == 0){ dp[i][j] = dp[i-1][j]; }else{ dp[i][j] = dp[i][j-1] + dp[i-1][j]; } } } cout<<dp[n][m]<<endl; } return 0; }
while True:
try:
a,b = map(int, raw_input().split())
a += 1
b += 1
#b = int(raw_input())
dp = [[0]*(b+1) for _ in range(a+1)]
dp[0][1] = 1
for i in range(1,a+1):
for j in range(1,b+1):
dp[i][j] = dp[i-1][j]+dp[i][j-1]
print dp[-1][-1]
except:
break
int numofstep(int max, int min)
{
if (max < min)
swap(max, min);
vector<int> arr(min+1, 1);
for (int i = 1; i <= max; ++i)
for (int j = 1; j <= min;++j)
arr[j] = arr[j] + arr[j - 1];
return arr[min];
}
int main()
{
int m, n;
while (cin >> m >> n)
cout << numofstep(m, n) << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
//void dfs()
int main()
{
int n,m;
while(cin>>n>>m)
{
vector<vector<int> > dp(m+1,vector<int>(n+1,0));
dp[0][0]=1;
for(int i=1;i<n+1;i++)
dp[0][i]=1;
for(int j=1;j<m+1;j++)
dp[j][0]=1;
for(int i=1;i<m+1;i++)
{
for(int j=1;j<n+1;j++)
{
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
int res = dp[m][n];
cout<<res<<endl;
}
system("pause");
return 0;
}