链接 :
http://acm.pku.edu.cn/JudgeOnline/problem?id=2704人生第一个dp,出现在了 2008-09-06 01:00:56 ,铭记这一时刻。(只能说我是菜鸟......but happy)
题意 :有一个小球,在矩阵上滚动,每次只能向右或者向下滚动,滚动的长度点是矩阵相应位置的数字,停止在滚动后的位置,不能滚动出图外面,0代表死胡同。问能停止在右下角的路径有多少条。
输入 :
4 //矩阵范围
2331 //矩阵
1213
1231
3110
想法 :开始想到bfs,交过,tle,但是后来想感觉即使不超时也不对,因为一个点有可能走过多次。是我自己的想法。后来越看越像dp了,最后
的目标点,到他的路径,是它左边的能走到它的点,和右边能走到它的点的路径的和,那么,这样推上去,就可以划分出它的子问题。(汗,这样写
大家不要笑话,因为我没有看过算法导论上的dp),因该是属于递推吧。再从上往下,一直推到目标点。就可以了。
只能向右 :横坐标单调递增
只能向下 :纵坐标单调递增
因为起点的能第一次达到的点的路径个数一定是1,所以最后存储结果的res[0][0] = 1;
核心代码 :
1. 因为横纵都是单调递增的,所以横向检测图中从点(small, column)到点(big, column)有没有0值的点,直接枚举每个位置,且不包含
两个端点
1 bool rok(int s, int b, int c){
2 int i;
3 for(i = s + 1; i < b; i++)
4 if(!map[i][c])return false;
5 return true;
6 }
2.因为横纵都是单调递增的,所以横向检测图中从点(row, small)到点(row, big)有没有0值的点,直接枚举每个位置,且不包含
两个端点
1 bool cok(int s, int b, int r){
2 int i, j;
3 for(i = s + 1; i < b; i++)
4 if(!map[r][i])return false;
5 return true;
6 }
3.递推的过程,从前往后,进行递推,如果不是只能向右或者向下,是不满足递推的条件的,不是单调的(不知道这里理解的对不对)
1 res[0][0] = 1; //起始点的第一个到达的点的路径个数一定是1。
2 for(i = 0; i < n; i++){
3 for(j = 0; j < n; j++){
4 if(res[i][j] && i != n - 1 || j != n - 1){ //可达,而且不是右下角的点
5 //向右
6 if(i + map[i][j] < n && rok(i, i + map[i][j], j)) //若在图内,且从起点到滚动停止点,图中没有0值点
7 res[i + map[i][j]][j] += res[i][j]; //滚动停止点的路径条数 = 起始点的路径条数 + 停止点的路径条数
8 //向下
9 if(j + map[i][j] < n && cok(j, j + map[i][j], i))
10 res[i][j + map[i][j]] += res[i][j];
11 }
12 }
13 }
ps : 题目提示c或者c++要用long long存储结果,可是"%ld"输出wa了n次,后来看了discuss后改的"%I64d"输出后ac
1 printf("%I64d\n", res[n - 1][n - 1]);