今天遇到遇到的一道题目,上学路线,咋一看是一个很简单的高中学的组合问题,但是难就难在题目所给的数据太大,long long 类型也会溢出,导致结果不准确,思考过后发现这个题目的正解应该是用DP来做
题目如下:
上学路线:
小D从家到学校的道路结构是这样的:由n条东西走向和m条南北走向的道路构成了一个n*m的网格,
每条道路都是单向通行的(只能从北向南,从西向东走)。已知小D的家在网格的左上角,学校在网
格的右下角。问小D从他的家到学校一共有多少种不同的上学路线。
小D上学路线数量,结果对1000000007取余。
两个正整数n和m,意义如题目所述。
100%的数据,n,m≤1000
按照排列组合的做法很简单,题目规定只能朝右和下走,如图所示时,向右需要走三步,向下需要走两步,总共走五步就能到达学校,我们只要在这五步中任意选两步向下走,其余三步向右走自然就能到达学校。所以答案即是
于是我们可以得出解的公式:
但是很遗憾就算long long类型也是存不下1000的阶乘的,我当时考虑了在计算阶乘时对中间结果进行取余,但是最后结果就会错了,于是还是得用到DP才行。
DP解法:
代码如下:
#include<iostream>
#pragma GCC optimize(3)
#pragma G++ optimize(3)
using namespace std;
int dot[1005][1005];
int main() {
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
dot[i][1] = 1;
for (int i = 1; i <= m; i++)
dot[1][i] = 1;
for (int i = 2; i <= n; i++)
for (int j = 2; j <= m; j++)
dot[i][j] = (dot[i - 1][j] + dot[i][j - 1]) % 1000000007;
cout << dot[n][m] % 1000000007 << endl;
return 0;
}
DP思路:
因为题目规定只能朝右或下走,所以每一点最多可以由左或上两个方向出发,假设每一点所保存的为到达该点的线路总和,所以得出公式:
dot[x][y]=dot[x-1][y]+dot[x][y-1]
同时在每一点的结果计算出来后,对该结果取余,防止溢出
图中每点的数字代表到达该点的所有方式
不得不说,这段代码很不美直接开了一个(1005,1005)的二维数组,这种暴力的做法真的很野蛮,鉴于OJ对时复杂度的高要求,实属没办法,于是我重新写了一段优美的代码如下:
#include<vector>
#include<iostream>
#define MODNUM 1000000007
using namespace std;
int main(){
int n,m;
cin>>n>>m;
vector<long long> line(m,1);
vector<vector<long long>> dot;
dot.resize(n);
dot[0]=line;
line.clear();
for(int i=1;i<n;i++){
line.resize(m);
line[0];
for(int j=1;j<m;j++){
line[j]=(line[j-1]+dot[i-1][j])%MODNUM;
dot[i]=line;
line.clear();
}
cout<<dot[n-1][m-1]<<endl;
return 0;
}