实验5.3某城市四面环水,只有左下角和右上角有一座可以到达城市的桥。城市被分成M*N个方形的房子,房子之间可以通路,如图5-4。随着城市的不断发展,原来的小房子逐渐被高楼大厦所取代,每座大厦可能会占用好几个小的房子,但仍保留原来的街道风格,如图5-4所示。
设左下角的坐标为(1,1),右上角坐标为(m, n),城市大厦采用左下角坐标和大厦占用的水平小房子数量和垂直小房子数量来描述。编写程序计算某人从左下角以最短的距离到到右上角一共有多少种不同的路径。
输入:
有多个测试用例。每个测试用例的第一行包含两个整数m, n,表示原来的城市垂直和水平方向上有多少个小房子。第2行为整数B,表示大厦的数量。其后
有B行,每一行包含4个整数x, y,z,b,分别表示大厦左下角坐标以及大厦垂直和水平方向上占用的小房子的数量。
输出:每一组测试数据输出一个整数,也就是从左下角到右上角总的路径数。样例输入:
5 6
1
2 4 3 2
样例输出:192
实验预习:
(1)图5-4中,设从左下角以最短的距离到达A1点的路径数为X1,到达A2的路径数为X2,求从左下角以最短距离到达B点的路径数为多少?
(2)图5-4中,设从左下角以最短的距离到C1点的路径数为Y1,到达C2的路径数为Y2,求从左下角以最短距离到达D点的路径数为多少?
(3)图5-4中,基于(1)的假设,求从左下角以最短路径到达C1, C2点的路径是多少?
(4)设计算法求解该问题,写出算法的伪代码,并分析算法的复杂度。
(5)根据算法,分析样例输入情况下的求解过程,写出求解过程中主要变量的变化过程。
(6)编写程序求解上述问题
4.1.1 1到3小题
(1)图5-4中,设从左下角以最短的距离到达A1点的路径数为X1,到达A2的路径数为X2,从左下角以最短距离到达B点的路径数为X1+ X2
(2)图5-4中,设从左下角以最短的距离到C1点的路径数为Y1,到达C2的路径数为Y2,求从左下角以最短距离到达D点的路径数为Y1+ Y2
(3)图5-4中,基于(1)的假设,求从左下角以最短路径到达C1, C2点的路径分别是X1+ 3、4X1+ 6X2+5
4.1.2 伪代码及时间复杂度
伪代码:
输入:第一行,m, n,第2行为整数B。其后有B行,每一行包含4个整数x, y,z,b
输出: dp[m+1][n+1]
- Begin
- for 0<j<=m+1,0<k<=n+1 do
- Obstacle[j][k]←0,dp[j][k]←0;
- for 0<i<B,0<j<=m+1,0<k<=n+1 do
- begin
- tempm←B[i].x,tempn←B[i].y,tm←B[i].z,tn←B[i].b;
- if (obstacle[j][k] == 0 && j > tempm && j <= tempm + tm-1&&k>tempn&&k<=tempn+tn-1) then
- Obstacle[j][k]←1;
- end
- While 0<j<=m+1&&obstacle[j][1]==0 do
- dp[j][1] = 1;
- while 0<k<=n+1&&obstacle[1][k]==0 do
- dp[1][k] = 1;
- for 1<j<=m+1,1<k<=n+1 do
- if (obstacle[j][k] == 0) then dp[j][k]=dp[j-1][k]+dp[j][k-1];
- print dp[m+1][n+1];
- End
时间复杂度分析:
m, n表示原来的城市垂直和水平方向上有多少个小房子。整数B表示大厦的数量。
初始化obstacle矩阵的时间复杂度是O(B*m*n)
初始化dp二维数组的时间复杂度是O(m+n)
求解dp二维数组的时间复杂度为O(m*n)
所以,总的时间复杂度为O(B*m*n)
4.1.3 手工求解样例
以表格形式展示dp数组,第i行j列的格子中的数字,表示从起点到坐标(i,j)的最短路径数。可推出从起点到右上角终点的最短距离的路径数为192条。
1 | 6 | 21 | 56 | 91 | 132 | 192 |
1 | 5 | 15 | 35 | 35 | 41 | 60 |
1 | 4 | 10 | 20 | 0 | 6 | 19 |
1 | 3 | 6 | 10 | 0 | 6 | 13 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
起点 | 1 | 1 | 1 | 1 | 1 | 1 |
4.1.4 程序实现
#include<iostream>
#define N 50
typedef struct Building
{
int bm, bn, mnum, nnum;//大厦左下角坐标,该大厦垂直方向和水平方向占有的房子数
}building;
int m, n;//右上角坐标
int bnum;//大厦数目
int obstacle[N][N]{ 0 };//
int dp[N][N]{0};
using namespace std;
int main()
{
cout << "input m,n,bnum:";
cin >> m >> n>>bnum;
building builds[N]{};
cout << "输入bnum行:";
for (int i = 1; i <= bnum; i++)
{
cin >> builds[i].bm >> builds[i].bn >> builds[i].mnum >> builds[i].nnum;
}
for(int i=1;i<=bnum;i++)
for (int j = 1; j <= m+1; j++)
for (int k = 1; k <= n+1; k++)
{
int tempm = builds[i].bm, tempn = builds[i].bn;
int tm = builds[i].mnum, tn = builds[i].nnum;
if (obstacle[j][k] == 0 && j > tempm && j <= tempm + tm-1&&k>tempn&&k<=tempn+tn-1)
{
obstacle[j][k] = 1;
}
}
for (int j = 1; j <= m+1 && obstacle[j][1] == 0; j++) dp[j][1] = 1;
for (int k = 1; k <= n+1 && obstacle[1][k] == 0; k++) dp[1][k] = 1;
for (int j = 2; j <= m+1; j++)
for (int k = 2; k <= n+1; k++)
{
if (obstacle[j][k] == 0)
{
dp[j][k] = dp[j - 1][k]+dp[j][k-1];
}
}
cout << dp[m+1][n+1];
return 0;
}