POJ 1185 炮兵阵地

非常有趣的一个问题,也困扰了我一段时间。


利用递归的话由于有大量重复计算的内容,所以可以使用动态规划,获取所有的需要计算的值。


题目内容中存在上两行都对当前行的选择存在影响,所以不同于一般的动态规划。我本来是希望将地图的每两行合并为一行,之后通过动态规划保存在确定上两行的情况下的最优解。不过在最坏情况下会超时,我计算了原先代码运算量为(2500 * 2500 * row)。


在看了某些大大关于状态压缩的文章才明白了。


首先由于地形和炮兵布置方案都只有两种状态,即 “H” 和 “P”  以及 “ 放置”  和 “不放”,所以可以用二进制表示,由于地图最大只有十列,所以最大值为1024(2 ^ 10)。


通过&符号可以实现二进制的 “与”功能,所以只需要用 地图的整数形态 & 方法的整数形态 ==  1 即可知道方法是否可以在地图上放置。 而  方法的整数形态 & 另一个方法即可知道上下行方法是否冲突。


之后是一行不冲突最多有60中可能(包括放置0个炮兵),如果不明白是怎么获得的,可以将下面代码Init函数中的count提出来看。


下面代码用g_lineState[i][j][k]保存第i行,本行方法序号为j,上一行方法序号为k的最优解(由顶至下)倍;

动态规划公式为遍历地图每一行i,本行方法序号j,上行方法序号k,上上行方法序号t,g_lineState[i][j][k] = max(g_lineState[i - 1][k][t] + 本行炮兵数);


新办法的运行时间为(row * 60 * 60 * 60),换算后比原先的代码块20倍;


下面代码的运行时间为188ms


/***********************
炮兵阵地
time (60 * 60 * 60 * g_row)
***********************/
#include"iostream"
#include"string.h"
#include"math.h"
using namespace std;

//宏定义
#define OneLineMax 60 //一行最大可行布置方案

//全局变量
char g_map[100][10]; //字符地图
int g_numMap[100]; //数字地图
int g_row; //地图的行
int g_col; //地图的列
int g_setStyle[OneLineMax]; //布置方案
int g_gunCount[OneLineMax]; //每个方案炮兵数目
int g_lineState[100][OneLineMax][OneLineMax]; //每行地图最优状态

//声明
void Init();
void DP();

//主函数
int main(void)
{
	Init();

	//读入地图信息
	cin >> g_row >> g_col;
	for(int i = 0; i < g_row; i++)
	{
		cin >> g_map[i];
		g_map[i][g_col] = 'H';

		//将地图转换为整数
		g_numMap[i] = 0;
		for(int j = 0; j < 10; j++)
		{
			if(g_map[i][j] == 'H')
			{
				g_numMap[i] += pow(2.0, 9 - j);
			}
		}
	}

	DP();

	//打印输出最优解
	int nowPos = 0;
	int lastPos = 0;
	for(int i = 0; i < OneLineMax; i++)
	{
		for(int j = 0; j < OneLineMax; j++)
		{
			if(g_lineState[g_row - 1][i][j] > g_lineState[g_row - 1][nowPos][lastPos])
			{
				nowPos = i;
				lastPos = j;
			}
		}
	}
	cout << g_lineState[g_row - 1][nowPos][lastPos];

	//system("pause");
	return 0;
}

//初始化函数
void Init()
{
	//初始化地图状态为‘H’
	memset(g_map, 'H', sizeof(g_map));

	//初始化最优状态为0
	memset(g_lineState, 0, sizeof(g_lineState));

	//获取所有布置方案并保存
	int count = 0;
	g_setStyle[count] = 0;
	g_gunCount[count++] = 0;
	for(int i1 = 0; i1 < 10; i1++)
	{
		int temp1 = pow(2.0, 9 - i1);
		g_setStyle[count] = temp1;
		g_gunCount[count++] = 1;

		for(int i2 = i1 + 3; i2 < 10; i2++)
		{
			int temp2 = pow(2.0, 9 - i2);
			g_setStyle[count] = temp1 + temp2;
			g_gunCount[count++] = 2;

			for(int i3 = i2 + 3; i3 < 10; i3++)
			{
				int temp3 = pow(2.0, 9 - i3);
				g_setStyle[count] = temp1 + temp2 + temp3;
				g_gunCount[count++] = 3;

				for(int i4 = i3 + 3; i4 < 10; i4++)
				{
					int temp4 = pow(2.0, 9 - i4);
					g_setStyle[count] = temp1 + temp2 + temp3 + temp4;
					g_gunCount[count++] = 4;
				}
			}
		}
	}
}

//动态规划获取最优解
void DP()
{
	//设置第一层地图的状态
	for(int i = 0; i < OneLineMax; i++)
	{
		//如果当前方案与地形冲突
		if(g_setStyle[i] & g_numMap[0])
		{
			continue;
		}

		g_lineState[0][i][0] = g_gunCount[i];
	}

	//设置之后几层的状态
	for(int i = 1; i < g_row; i++)
	{
		for(int j = 0; j < OneLineMax; j++)
		{
			//如果方案与地图冲突就跳到下一步
			if(g_setStyle[j] & g_numMap[i])
			{
				memset(g_lineState[i][j], 0, sizeof(g_lineState[i][j]));
				continue;
			}

			for(int k = 0; k < OneLineMax; k++)
			{
				//如果上行方案与本行方案发生冲突
				if(g_setStyle[j] & g_setStyle[k])
				{
					g_lineState[i][j][k] = 0;
					continue;
				}

				for(int t = 0; t < OneLineMax; t++)
				{
					//如果上上行方案与本行方案发送冲突
					if(g_setStyle[j] & g_setStyle[t])
					{
						continue;
					}

					//判断当前选择是否较优
					int temp = g_gunCount[j] + g_lineState[i - 1][k][t];
					if(temp > g_lineState[i][j][k])
					{
						g_lineState[i][j][k] = temp;
					}
				}
			}
		}
	}
}



测试数据


5 4
PHPP
PPHH
PPPP
PHPP
PHHP
100 10
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
PPPPPPPPPP
334


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值