C语言程序设计100例之20过河卒Word格式文档下载.docx
- 文档编号:16709199
- 上传时间:2022-11-25
- 格式:DOCX
- 页数:14
- 大小:49.33KB
C语言程序设计100例之20过河卒Word格式文档下载.docx
《C语言程序设计100例之20过河卒Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《C语言程序设计100例之20过河卒Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。
0,j>
0,g[x,y]=0)//递推式
(2)源程序。
#include<
stdio.h>
intmain()
{
inti,j,x,y,n,m,forbidden[51][51];
longintans[51][51];
intdx[8]={-2,-1,1,2,2,1,-1,-2};
intdy[8]={1,2,2,1,-1,-2,-2,-1};
scanf("
%d%d%d%d"
&
n,&
m,&
x,&
y);
for(i=0;
i<
=n;
i++)
for(j=0;
j<
=m;
j++)
{
forbidden[i][j]=0;
ans[i][j]=0;
}
ans[0][0]=1;
forbidden[x][y]=1;
8;
i++)
if(x+dx[i]>
=0&
&
x+dx[i]<
=n&
y+dy[i]>
y+dy[i]<
=m)
forbidden[x+dx[i]][y+dy[i]]=1;
for(i=1;
i<
i++)
if(forbidden[i][0]==0)
ans[i][0]=1;
elsebreak;
if(forbidden[0][i]==0)
ans[0][i]=1;
for(j=1;
j<
j++)
if(forbidden[i][j]==0)
ans[i][j]=ans[i-1][j]+ans[i][j-1];
printf("
%ld\n"
ans[n][m]);
return0;
}
习题20
20-1马的行走路径
问题描述
设有一个n*m的棋盘(2<
=n<
=50,2<
=m<
=50),在棋盘上任一点有一个中国象棋马,如图2(a)所示。
马行走的规则为:
(1)马走日字;
(2)马只能向右走,即如图2(b)所示的4种走法。
编写一个程序,输入n和m,找出一条马从棋盘左下角(1,1)到右上角(n,m)的路径。
例如:
输入n=4、m=4时,输出路径(1,1)->
(2,3)->
(4,4)。
这一路经如图2(c)所示。
若不存在路径,则输出"
No!
"
(a)(b)(c)
图2棋盘及马儿的行走
一行两个数据,表示终点的位置坐标。
一条可行的行走路径。
如果可行的行走路径有多条,任意输出一条即可。
。
1010
(1,1)->
(3,5)->
(4,7)->
(5,9)->
(6,7)->
(7,9)->
(9,8)->
(10,10)
(1)编程思路
先将棋盘的横坐标规定为i,纵坐标规定为j,对于一个n×
m的棋盘,i的值从1到n,j的值从1到m。
棋盘上的任意点都可以用坐标(i,j)表示。
对于马的移动方法,用变量k来表示四种移动方向(1、2、3、4);
而每种移动方法用偏移值来表示,并将这些偏移值分别保存在数组dx和dy中,如表1所示。
表14种移动方法对应偏移值
K
Dx[k]
Dy[k]
1
2
-1
3
4
-2
根据马走的规则,马可以由(i-dx[k],j-dy[k])走到(i,j)。
只要马能从(1,1)走到(i-dx[k],j-dy[k]),就一定能走到(i,j),当然,马走的坐标必须保证在棋盘上。
以(n,m)为起点向左递推,当递推到(i-dx[k],j-dy[k])的位置是(1,1)时,就找到了一条从(1,1)到(n,m)的路径。
程序中可用一个二维数组a表示棋盘,使用倒推法,从终点(n,m)往左递推,设初始值a[n][m]为(-1,-1)(表示终点),如果从(i,j)一步能走到(n,m),就将(n,m)存放在a[i][j]中。
如表2所示,a[3][2]和a[2][3]的值是(4,4),表示从这两个点都可以到达坐标(4,4)。
从(1,1)可到达(2,3)、(3,2)两个点,所以a[1][1]存放两个点中的任意一个即可。
递推结束以后,如果a[1][1]值为(0,0)说明不存在路径,否则a[1][1]值就是马走下一步的坐标,以此顺推输出路径。
表2N=4,M=4时,数组a的赋值情况
A[4][4]={-1,-1}
A[2][3]={4,4}
A[3][2]={4,4}
A[1][1]={2,3}
intdx[5]={0,2,2,1,1},dy[5]={0,1,-1,2,-2};
structpoint
intx;
inty;
};
structpointa[51][51];
inti,j,n,m,k;
for(i=0;
51;
for(j=0;
a[i][j].x=a[i][j].y=0;
%d%d"
m);
a[n][m].x=-1;
//标记为终点
a[n][m].y=-1;
for(i=n;
i>
=2;
i--)//倒推
if(a[i][j].x!
=0)
for(k=1;
k<
=4;
k++)
a[i-dx[k]][j-dy[k]].x=i;
a[i-dx[k]][j-dy[k]].y=j;
}
if(a[1][1].x==0)
printf("
No!
\n"
);
else//存在路径
i=1;
j=1;
(%d,%d)"
i,j);
while(a[i][j].x!
=-1)
k=i;
i=a[i][j].x;
j=a[k][j].y;
->
20-2方格取数
(一)
设有N×
N的方格图(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
如下所示(见样例):
A
5
7
8
13
14
21
15
B
某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点走到B点,试找出1条这样的路径,使得取得的数之和为最大。
输入的第一行为一个整数N(表示N×
N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。
一行单独的0表示输入结束。
只需输出一个整数,表示找出的1条路径上取得的最大的和。
2313
266
357
4414
5221
564
6315
7214
000
36
(1)编程思路。
因为行走的方向是:
可以向下行走,也可以向右走。
因此,位置(i,j)可以由上边的格子(i-1,j)走到,也可以由左边的格子(i,j-1)走到。
设f[i][j]表示走到格子(i,j)处所取方格数的最大值,a[x][y]表示格子(x,y)上的数字。
显然有
f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];
初始时f[1][1]=a[1][1]。
(2)源程序。
intmax(inta,intb)
{returna<
b?
b:
a;
intf[10][10]={0},a[10][10]={0};
intn;
%d"
n);
while
(1)
intx,y,w;
scanf("
%d%d%d"
y,&
w);
if(x==0&
y==0&
w==0)break;
a[x][y]=w;
f[1][1]=a[1][1];
inti,j;
for(j=1;
{
f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];
%d\n"
f[n][n]);
20-3方格取数
(二)
此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
只需输出一个整数,表示2条路径上取得的最大的和。
67
(1)编程思路1。
本题要求找到2条从(1,1)到(n,n)的路径,被取走的格子里的数变为0,使得在两条路径上格子中数之和最大时,就成为了“二取方格数”问题。
最容易想到的就是先后做两次单条路径“方格取数”,这一算法的本质是贪心,但这是错误的,反例如下:
0
2
8
贪心:
第一路径:
3->
4->
8->
2 (17) 第二路径:
5(5) 总和为22
事实上我们可以将所有的数都取完,总和为24。
解决“二取方格数”问题需要用到“多进程DP”。
即解决本题时,由于此人从A点到B点共走两次,要找出2条这样的路径,因此可以考虑为两个人同时从A走到B。
设f[i][j][k][l]为第一个人走到(i,j),第二个人走到(k,l)时方格取数能达到的最大值,a[x][y]表示格子(x,y)上的数字。
状态转移情况如下:
1)两个人同时向右走
f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k-1][l]+a[i][j]+a[k][l]);
2)两个人同时向下走
f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l-1]+a[i][j]+a[k][l]);
3)两个人分别向右和向下走
f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l-1]+a[i][j]+a[k][l]);
4)两个人分别向下和向右走
f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k-1][l]+a[i][j]+a[k][l]);
当然,若两人走到了同一个格子,即(i,j)和(k,l)是同一个点,f[i][j][k][l]值还要减去a[i][j]。
两个人都走到(n,n)格子时,得到答案,即f[n][n][n][n]为所求。
(2)源程序1。
intf[10][10][10][10]={0},a[10][10]={0};
f[1][1][1][1]=a[1][1];
inti,j,k,l;
for(k=1;
for(l=1;
l<
l++)
{
f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k-1][l]+a[i][j]+a[k][l]);
f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l-1]+a[i][j]+a[k][l]);
f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l-1]+a[i][j]+a[k][l]);
f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k-1][l]+a[i][j]+a[k][l]);
if(i==k&
j==l)f[i][j][k][l]-=a[i][j];
}
f[n][n][n][n]);
(3)编程思路2。
按思路1的方法,由于状态总共有n^4种,所以时间复杂度为O(n^4)。
如果让两个人同时从(1,1)处出发,并同时向前延伸,那么当两个人都走了k步,两条路径都已经各自包含k个方格时,两条路径的末端必同在整个矩阵的第k条对角线上。
如下图3所示。
2
4
5
6
7
3
图3行走对角线示意图
由图3可知,走1步可到达(1,1)格子(标注为2),走两步可到达(1,2)或(2,1)格子(标注为2),走三步可到达(1,3)、(2,2)或(3,1)格子(标注为4),……。
由图可知,对于每一条路径,向右延伸的格子数+向下延伸的格子数=k(定值),也就是末端两个格子的纵横坐标之和=k。
所以我们只需要知道两路径末端所在的行编号x1,x2以及两末端所在对角线编号k,就可以确定末端节点的位置(x1,k-x1),(x2,k-x2)。
这样,可以只枚举对角线、x1和x2。
设状态f[l][x1][x2]第一个人横坐标为x1(即第一个路径末端在第x1行),第二个人横坐标为x2(即第二路径末端在第x2行),且两末端同在第k条对角线上时的最优解。
到达状态f[l][x1][x2]有有4种可能:
1)第1人从x1的左边向右到达x1,第2人从x2的左边向右到达x2,其前一状态应为f[k-1][x1-1][x2-1];
2)第1人从x1的上边向下到达x1,第2人从x2的上边向下到达x2,其前一状态应为f[k-1][x1][x2];
3)第1人从x1的左边向右到达x1,第2人从x2的上边向下到达x2,其前一状态应为f[k-1][x1-1][x2];
4)第1人从x1的上边向下到达x1,第2人从x2的左边向右到达x2,其前一状态应为f[k-1][x1][x2-1];
这样,可以得到状态转移方程:
tmp=max(max(f[k-1][x1-1][x2-1],f[k-1][x1][x2]),max(f[k-1][x1-1][x2],f[k-1][x1][x2-1]));
f[k][x1][x2]=max(f[k][x1][x2],tmp+a[x1][k-x1]+a[x2][k-x2]);
同样,如果点(x1,k-x1)和(x2,k-x2)重合了,需要减去一个点中的数(每个点只能取一次)。
(4)源程序2。
intf[19][10][10]={0},a[10][10]={0};
intd=n*2;
f[2][1][1]=a[1][1];
for(inti=3;
=d;
intc=i<
n+1?
i:
n+1;
ints=i>
n?
i-n:
1;
for(intj=s;
c;
for(intk=s;
intx1=j,x2=k,y1=i-j,y2=i-k;
inttmp=max(max(f[i-1][x1-1][x2-1],f[i-1][x1][x2]),
max(f[i-1][x1-1][x2],f[i-1][x1][x2-1]));
f[i][x1][x2]=max(f[i][x1][x2],tmp+a[x1][y1]+a[x2][y2]);
if(x1==x2&
y1==y2)f[i][x1][x2]=f[i][x1][x2]-a[x1][y1];
}
f[d][n][n]);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言程序设计 100 20 过河