【算法设计与分析基础】20、动态规划-硬币搜集问题

题目:

在n*m格木板中放有一些硬币,每格的硬币数目最多为一个。在木板左上方的一个机器人需要搜集
尽可能多的硬币并把他们带到右下方的单元格,每一步,机器人可以从当前的位置向右移动一格
 或者向下移动一格,当机器人遇到一个有硬币的单元格的时,就会将这枚硬币搜集起来

 

 

 

解题:

硬币收集的时候,我们 从结果状态开始看,当搜集当前硬币的时候,只有两种方式,从上往下搜集,或者从左向右搜集
也就是当前f[i,j] = max{f[i, j - 1], f[i - 1, j]},初始化第一行和第一列,从第二行和列开始遍历
就可以动态规划所有的中间状态,最后获取最后的位置的地方的和,即便是搜集到的最大的和,并且过程的路径可以根据动态规划中间数组输出

 

 

package cn.xf.algorithm.ch08DynamicProgramming;

import java.util.ArrayDeque;
import java.util.Deque;

import org.junit.Test;

import cn.xf.algorithm.ch08DynamicProgramming.vo.CompareIndexVo;
import cn.xf.algorithm.ch08DynamicProgramming.vo.ResultVo;

/**
 * 硬币搜集问题
 * 
 * 在n*m格木板中放有一些硬币,每格的硬币数目最多为一个。在木板左上方的一个机器人需要搜集
 * 尽可能多的硬币并把他们带到右下方的单元格,每一步,机器人可以从当前的位置向右移动一格
 * 或者向下移动一格,当机器人遇到一个有硬币的单元格的时,就会将这枚硬币搜集起来
 * 
 * .
 * 
 * @版权:福富软件 版权所有 (c) 2017
 * @author xiaof
 * @version Revision 1.0.0
 * @see:
 * @创建日期:2017年8月4日
 * @功能说明:
 *
 */
public class CollectCoins {
 
	//硬币收集的时候,我们 从结果状态开始看,当搜集当前硬币的时候,只有两种方式,从上往下搜集,或者从左向右搜集
	//也就是当前f[i,j] = max{f[i, j - 1], f[i - 1, j]},初始化第一行和第一列,从第二行和列开始遍历
	//就可以动态规划所有的中间状态,最后获取最后的位置的地方的和,即便是搜集到的最大的和,并且过程的路径可以输出
	public ResultVo robotCoinCollection(int coins[][]) {
		if(coins == null || coins.length <= 0 || coins[0].length <= 0) {
			return null;
		}
//		Deque deque = new ArrayDeque();
		//创建存储硬币动态规划数组
		int allRows = coins.length;
		int allColumns = coins[0].length;
		int resultF[][] = new int[allRows][allColumns];
		//首先初始化,起始位置和第一行
		resultF[0][0] = coins[0][0];
//		String curPath = "<0,0>";
//		deque.push(curPath);
		for(int j = 1; j < allColumns; ++j) {
			resultF[0][j] = resultF[0][j - 1] + coins[0][j];
		}
		//双循环,遍历整个地图
		for(int i = 1; i < allRows; ++i) {
			//顺路初始化每一行的第一个
			resultF[i][0] = resultF[i - 1][0] + coins[i][0];
			//遍历所有列
			for(int j = 1; j < allColumns; ++j) {
				//选择路径比较大的进入队列
				//当搜集当前硬币的时候,只有两种方式,从上往下搜集,或者从左向右搜集,那么就是把从上过来的和从右边过来的进行比较之后,选择硬币搜集比较大的位置为路径
				CompareIndexVo compareIndexVo = getMax(resultF[i][j - 1], resultF[i - 1][j]);
				resultF[i][j] = compareIndexVo.getResult() + coins[i][j];
				//路径设计
//				if(compareIndexVo.getIndex() == 1) {
//					//如果是第一个参数比较大
//					curPath = "<" + i +", " + (j - 1) + ">";
//				} else {
//					curPath = "<" + (i - 1) +", " + j + ">";
//				}
//				deque.push(curPath);
			}
		}
		ResultVo resultVo = new ResultVo();
		resultVo.setResultF(resultF);
//		resultVo.setDeque(deque);
		return resultVo;
	}
	
	public static CompareIndexVo getMax(int a, int b) {
		CompareIndexVo vo = new CompareIndexVo();
		if(a < b) {
			vo.setResult(b);
			vo.setIndex(2);
		} else {
			vo.setResult(a);
			vo.setIndex(1);
		}
		
		return vo;
	}
	
	@Test
	public void test1() {
		CollectCoins collectCoins = new CollectCoins();
		int coins[][] = {{0,0,0,0,1,0},{0,1,0,1,0,0},{0,0,0,1,0,1},{0,0,1,0,0,1},{1,0,0,0,1,0}};
		ResultVo resultVo = collectCoins.robotCoinCollection(coins);
		//输出路径,以及最大值
		System.out.println("搜集到的最大硬币是:" + resultVo.getResultF()[coins.length - 1][coins[0].length - 1]);
		System.out.print("路径是:");
		//循环从结果数组中查询出对应的路径
		int curI = 0; int curJ = 0;
		int resultF[][] = resultVo.getResultF();
		System.out.print("<0,0>");
		while(curI < coins.length && curJ < coins[0].length) {
			//比较向下和向右的大小
			int goDown; int goRight;
			int i,j;
			if(curI == coins.length -1) {
				//如果i极限
				goDown = -1;
			} else {
				goDown = resultF[curI + 1][curJ];
			}
			
			if(curJ == coins[0].length -1) {
				//如果i极限
				goRight = -1;
			} else {
				goRight = resultF[curI][curJ + 1];
			}
			//只能向右或向下走 !(goDown == -1 && goDown == -1)
			//两个同时到了末尾,就不用输出了
			if(!(goDown == -1 && goDown == -1)) {
				if(goDown > goRight) {
					//向下走,如果是因为向右到头le
					System.out.print(" = <" + (curI + 1) + "," + curJ + ">");
					curI += 1;
				} else {
					System.out.print(" = <" + (curI) + "," + (curJ + 1) + ">");
					curJ += 1;
				}
			} else {
				//两个同时到头,跳出循环
				break;
			}
		}
	}
	
}

  

 

结果:

 

 

posted @ 2017-08-06 19:03  cutter_point  阅读(1055)  评论(0编辑  收藏  举报