动态规划-阿里笔试

动态规划-阿里笔试_第1张图片


答案:117.
分析:
状态压缩+动态规划+记忆化搜索
状态压缩:用int型数据保存拼图过程中的图案。二进制表示的倒数第i位表示2*7地面中第i个格子是否被地板铺上与否。
动态规划:dp[r][c][sta]表示在形状sta的基础上,在(r,c)位置开始拼,拼满整个地面的情况种类数目。那么状态转移方程就是:
[java]  view plain copy
  1. if (c + 1 < COLUMN)  
  2.     dp[r][c][sta] += DP(r, c + 1, staTemp);  
  3. else  
  4.     dp[r][c][sta] += DP(r + 10, staTemp);//staTemp表示铺上某块地板后的新图案。  

记忆化搜索:已计算过的不要重复计算,不然开销大到不能承受。
结构体设计:有Point与Shape,后者表示砖的形状。因为每块砖的面积都大于1*1,所以我们可以缺省一个,省点代码量。见下图:
动态规划-阿里笔试_第2张图片

代码:

/**
 * 单选第三题
 * 状态压缩+动态规划+记忆化搜索
 */
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.out;

//r为行序,c为列序,从上到下,从左到右,都从0开始
//( (stat >> (r * COLUMN + c) ) & 1 ) 表示r行c列是否被地板覆盖——————1表示覆盖;0表示未覆盖。
class Point {
	int x, y;

	public Point(int a, int b) {
		x = a;
		y = b;
	}
}

class Shape {
	List p;
}

public class A {
	boolean DEBUG=true;
	final int ROW = 2, COLUMN = 7;
	final int FULL = (1 << ROW * COLUMN) - 1;

	int[][][] dp = new int[ROW][COLUMN][1 << (ROW * COLUMN + 1)];
	boolean[][][] vis = new boolean[ROW][COLUMN][1 << (ROW * COLUMN + 1)];
	/** 地板可以旋转,总共六种形状 */
	Shape[] SH = new Shape[6];

	public static void main(String[] args) {
		A obj = new A();
		obj.initial();
		int ans = obj.DP(0, 0, 0);
		out.println("ANS:" + ans);
	}

	/** 表示尝试过程中合理的情况 */
	void debug(boolean isDebug, int sta, int staTemp) {
		if(!isDebug) 
			return;
		show(sta);
		out.println("--->");
		show(staTemp);
		out.println();
	}

	void initial() {
		for (int i = 0; i < SH.length; i++) {
			SH[i] = new Shape();
			SH[i].p = new ArrayList();
		}

		SH[0].p.add(new Point(0, 1));

		SH[1].p.add(new Point(1, 0));

		SH[2].p.add(new Point(0, 1));
		SH[2].p.add(new Point(1, 1));

		SH[3].p.add(new Point(1, 1));
		SH[3].p.add(new Point(1, 0));

		SH[4].p.add(new Point(1, 0));
		SH[4].p.add(new Point(1, -1));

		SH[5].p.add(new Point(1, 0));
		SH[5].p.add(new Point(0, 1));
	}

	/** 图形化输出sta状态表示的图案 */
	void show(int sta) {
		for (int i = 0; i < ROW * COLUMN; i++) {
			if (i == COLUMN)
				out.println();
			out.print(sta & 1);
			sta >>= 1;
		}
		out.println();
	}

	/** 返回值表示——————在形状sta的基础上,在(r,c)位置开始拼,拼满整个地面的情况种类 */
	int DP(int r, int c, int sta) {

		// 如果全铺满
		if (sta == FULL)
			return 1;
		// 如果越界
		if (r >= ROW || r < 0 || c >= COLUMN || c < 0)
			return 0;

		// 记忆化搜索
		if (vis[r][c][sta])
			return dp[r][c][sta];
		vis[r][c][sta] = true;

		boolean isRecovered = ((sta >> (r * COLUMN + c)) & 1) == 1;
		if (!isRecovered) {
			// 6种形状的地板
			for (int i = 0; i < 6; i++) {
				boolean flag = true;

				// 每种形状的地板都有1*1的部分是默认放在(r,c)位置的
				int staTemp = sta + (1 << (r * COLUMN + c));

				for (int j = 0; j < SH[i].p.size(); j++) {
					int pos = (SH[i].p.get(j).x + r) * COLUMN
							+ SH[i].p.get(j).y + c;
					if (SH[i].p.get(j).x + r < 0 || SH[i].p.get(j).x + r >= ROW
							|| SH[i].p.get(j).y + c < 0
							|| SH[i].p.get(j).y + c >= COLUMN ||
							// 这个位置已经被其他地板覆盖
							((staTemp >> pos) & 1) == 1) {
						flag = false;
						break;
					}
					staTemp += (1 << pos);
				}// for-每块木板的多个单位
					// 如果该块木板可以放下,代入状态转移方程
				if (flag) {
					debug(DEBUG, sta, staTemp);
					// 同行从左往右放,放满了再起一行从第0列放
					if (c + 1 < COLUMN)
						dp[r][c][sta] += DP(r, c + 1, staTemp);
					else
						dp[r][c][sta] += DP(r + 1, 0, staTemp);
				}
			}// for-铺不同的木板
		} else
		// 此处不能放继续尝试,原图形状态sta保持不变
		{
			if (c + 1 < COLUMN)
				dp[r][c][sta] += DP(r, c + 1, sta);
			else
				dp[r][c][sta] += DP(r + 1, 0, sta);
		}
		return dp[r][c][sta];
	}
}
//ANS:117

你可能感兴趣的:(动态规划)