数塔java_数塔 - Wei_java - 博客园

本文通过数塔问题介绍了一种动态规划的思考方法,即假设后续步骤的结果已知来选择最优路径。通过Java代码展示了如何求解数塔问题,并以免费馅饼问题和滑雪问题为例进一步解释了这种动态规划的思路。
摘要由CSDN通过智能技术生成

350946f71a32522a113caf2fa13ed242.png

数塔问题 :要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

分析:站在位置9,我们可以选择沿12方向移动,也可以选择沿着15方向移动,现在我们假设“已经求的”沿12方向的最大值x和沿15方向的最大值y,那么站在9的最大值必然是:Max(x,y) + 9。

因此不难得出,对于任意节点i,其状态转移方程为:m[i] = Max(a[i的左孩子] , a[i的右孩子]) + a[i]

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 #include

2 using namespacestd;3

4 #define Max(a,b) ((a)>(b) ? (a):(b))

5

6 int a[1000];7

8 intmain(){9 intt,n,i,k,j,m;10 cin>>t;11 for(i=0;i

13 cin>>n;14 k=n*(n+1)/2;15 for(j=1;j<=k;j++){16 cin>>a[j];17 }18 k = k - n; //从下往上 从倒数第二行最后一个元素开始依次找到其到最后一行的最大路径值 一直找到第一行第一个元素即是最大值

19 for(i=k,j=0;i>=1;i--){20 a[i]=a[i]+Max(a[i+n],a[i+n-1]);21 if(++j==n-1)22 {n--;23 j=0;}24 }25 cout<

27 }28 return 0;29 }

View Code

首先什么是“数塔类型”?从某一点转向另一点或者说是从某一状态转向另一状态,有多种选择方式(比如这里的9->12 , 9->15),从中选取一条能产生最优值的路径。

这类问题的思考方法:假设后续步骤的结果已知,比如这里假设已经知道沿12方向的最大值x和沿15方向的最大值y。

接下来看几个题,加深印象吧

1.免费馅饼问题

5 (起始位置)

4       |      5       |       6

3   4   5  |  4   5   6  |  5   6   7

..................

和“数塔”一样,它也是从某一点出发,有多个选择的问题(往前走一步,呆在原地,往后走一步)从中选择一条最优值路径(获得馅饼最多)。还是按照“数塔”的思考方式,我们可以假设“已经求得”下一个站在位置4获得的最大值x和呆在原地获得的最大值y以及站在位置6获得的最大值z,那么对于起始位置5获得最大值就是Max(x,y,z) ,因此可以得到状态转移方程为:m[t][x] = Max(m[t+1][x-1] , m[t+1][x] , m[t+1][x+1])

并且我们可以通过“列表格”的方式,自底向上求解:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 代码2

3 #include

4 #include

5

6 #define N 100000

7 int a[N][11];8

9 int Max(int a , int b , intc)10 {11 intn;12

13 n = a > b ?a : b;14

15 return n > c ?n : c;16 }17

18

19 int main(void)20 {21 intn , x , t , max , i;22

23

24 while(scanf("%d",&n))25 {26 if(!n) break;27

28 max = 0;29 memset(a , 0 , sizeof(a));30 for(i = 0 ; i < n ; i++)31 {32 scanf("%d%d",&x,&t);33 a[t][x] += 1;34 if(t > max) max =t;35 }36

37

38 //DP

39 for(t = max - 1 ; t >= 0 ; t--)40 {41 a[t][0] += Max(0 , a[t + 1][0] , a[t + 1][1]) ;42

43 for(x = 1 ; x < 10 ; x++)44 {45 a[t][x] += Max(a[t + 1][x - 1] , a[t + 1][x] , a[t + 1][x + 1]) ;46 }47 a[t][10] += Max(a[t + 1][9] , a[t + 1][10] , 0) ;48 }49 printf("%d\n",a[0][5]);50 }51

52 return 0;53 }

View Code

2.滑雪问题

左   A    右

依然和“数塔”一样,从某一点出发,面临多个选择(往上,往左,往下,往右)从中选择一条最优值路径(滑雪距离最长)

若对A点求,很显然它的最大值就为: Max(上,右,下,左) + 1

因此对于任意位置[i,j], 其状态转移方程为:m[i][j] = Max(m[i-1][j] , m[i][j+1] , m[i+1][j] , m[i][j-1]) + 1

由于这道题很难画出它的路径图(起点和终点都不知道)因此很难用“列表格”的方式自底向上求解,因此我们采用备忘录法:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 代码2

3 #include

4 #include

5 #define N 101

6

7 inta[N][N] , m[N][N] , r , c;8

9 int OK(int i ,intj)10 {11 return (i >= 1 && i <= r && j >= 1 && j <=c);12 }13

14 int search(int i , intj)15 {16 intk;17

18 if(m[i][j] > 0) returnm[i][j];19

20 if(OK(i - 1, j) && a[i][j] > a[i - 1][j])21 {22 k = search(i - 1, j) + 1;23 m[i][j] = m[i][j] < k ?k : m[i][j];24 }25 if(OK(i, j + 1) && a[i][j] > a[i][j + 1])26 {27 k = search(i, j + 1) + 1;28 m[i][j] = m[i][j] < k ?k : m[i][j];29 }30 if(OK(i + 1, j) && a[i][j] > a[i + 1][j])31 {32 k = search(i + 1, j) + 1;33 m[i][j] = m[i][j] < k ?k : m[i][j];34 }35 if(OK(i, j - 1) && a[i][j] > a[i][j - 1])36 {37 k = search(i, j - 1) + 1;38 m[i][j] = m[i][j] < k ?k : m[i][j];39 }40

41 returnm[i][j] ;42 }43

44 int main(void)45 {46 inti , j , k , t;47

48 while(scanf("%d%d", &r, &c) !=EOF)49 {50 for(i = 1 ; i <= r ; i++)51 for(j = 1 ; j <= c ; j++)52 scanf("%d", &a[i][j]);53

54 memset(m, 0, sizeof(m)); k = 0;55 for(i = 1 ; i <= r ; i++)56 for(j = 1 ; j <= c ; j++)57 {58 t =search(i, j);59 k = k < t ?t : k;60 }61

62 printf("%d\n", k + 1);63

64 }65 return 0;66 }

View Code

3.Worm问题,这题和免费馅饼几乎是一样的,我们同样可以使用“列表格”的方式自底向上求解:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 代码2

3 #include

4 #include

5 #define N 101

6

7 inta[N][N] , m[N][N] , r , c;8

9 int OK(int i ,intj)10 {11 return (i >= 1 && i <= r && j >= 1 && j <=c);12 }13

14 int search(int i , intj)15 {16 intk;17

18 if(m[i][j] > 0) returnm[i][j];19

20 if(OK(i - 1, j) && a[i][j] > a[i - 1][j])21 {22 k = search(i - 1, j) + 1;23 m[i][j] = m[i][j] < k ?k : m[i][j];24 }25 if(OK(i, j + 1) && a[i][j] > a[i][j + 1])26 {27 k = search(i, j + 1) + 1;28 m[i][j] = m[i][j] < k ?k : m[i][j];29 }30 if(OK(i + 1, j) && a[i][j] > a[i + 1][j])31 {32 k = search(i + 1, j) + 1;33 m[i][j] = m[i][j] < k ?k : m[i][j];34 }35 if(OK(i, j - 1) && a[i][j] > a[i][j - 1])36 {37 k = search(i, j - 1) + 1;38 m[i][j] = m[i][j] < k ?k : m[i][j];39 }40

41 returnm[i][j] ;42 }43

44 int main(void)45 {46 inti , j , k , t;47

48 while(scanf("%d%d", &r, &c) !=EOF)49 {50 for(i = 1 ; i <= r ; i++)51 for(j = 1 ; j <= c ; j++)52 scanf("%d", &a[i][j]);53

54 memset(m, 0, sizeof(m)); k = 0;55 for(i = 1 ; i <= r ; i++)56 for(j = 1 ; j <= c ; j++)57 {58 t =search(i, j);59 k = k < t ?t : k;60 }61

62 printf("%d\n", k + 1);63

64 }65 return 0;66 }

View Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值