Nyoj 81 炮兵阵地

解题报告:

1.算法 : 
(1)首先,看到这个题目想到的是暴力搜索,无所谓深搜还是宽搜,都需要对所有的情况进行穷举,10*100 的格子,这样穷举的话基本上会超时。想到用贪心法,但是贪心法的结果是不正确的

 
(2)于是想到动态规划,动态规划的重点是找状态转移方程,需要状态记录的数组 f(因为最终要求的是大炮个数,所以 f 的值记录当前状态的大炮个数)。从题目的数据中来看因为 m<10,所以以行划分状态比较合理,这样有 n 行,就用记录状态的数组 f 的第一个下标记录行号因为本题的大炮的射程是 2 格,所以,第 i 行和他的前两行都有关系,也就是说第 i 行的状态要根据前两行的状态来得到而不是前一行,于是在记录状态的时候要记录两行的状态(思考为什么),这样, f 数组的第二个下标和第三个下标分别是第 i 行的状态和第 i-1 行的状态。这样可以列出状态转移的方程式: f[i][x][y]=max{f[i -1][y][z]}+c[x] (其中 z 为 i-2 行所有的状态,c[x]为 x 状态中的大炮的个数) 


(3)有个状态转移方程这个精髓,就来说说这个题目的实现了: 
   a.读入数据 n,m 和矩阵数据,并且把每一行的数据以 2 进制的形式(有山处为 1,平原处为0)保存为一个 int 类型,即 row 数组。(read_in 函数)

 
   b.因为同一行的大炮不能相互射击,所以可以把每一行可能的状态列举出来,s 表示一行的所有状态,c 与 s 对应,表示对应状态的大炮个数,s_sum 是 s 的大小,即总的状态个数(m最大为 10 时,一行的状态最多,为 60 种,于是,数组只要开到 60)这些都是为下面的计算节省时间。计算一行可能的所有状态是通过深搜实现的,即 dfs 函数。(creat_s 函数) 


   **c.最重要的动态规划环节,首先判断特例,只有一行时的做法是将 s 中所有的状态分别和第一行的状态 row[0]做&操作以判断是否这个状态可以符合只在平原上放大炮的条件如果&的结果为 0,即没有在有山的地方放置大炮,反之则不然在符合条件的状态中找一个大炮数最多的状态作为返回值。如果不止一行,就对 f[1][][]的数组进行操作,第一参数为 1表示后面两个参数是第 1 行和第 0 行的状态,对第 1 行的状态(i)和第 0 行的状态(j)遍历,如果两行均符合平原山地条件以及两行不冲突条件(即两行之间没有大炮能相互攻击),则将f[1][i][j]的值设为 c[i]+c[j],否则为 0。接着要做的事情是从 i=2 开始,通过状态转移方程得到后面的所有状态。最后找出最后两行状态的 f 的最大值,这个值即为所求结果。 

2.实现 
(1)对于位操作,如果要获得第 i 位的数据,判断((data&(0X< (2)在构建 s 的时候,通过递归进行深搜。对于深搜的代码要熟练掌握,主要是递归条件和终止条件。 
(3)动态规划的时候要注意将特殊情况列出,比如这一题的 n=1 的情况。 
(4)在多重循环的时候要注意剪枝的运用,如 if((s[x]&row[i])!=0)cont inue;这一句就避免了下面的两重循环的时间浪费。

 

#include 
#include 
#include 

using namespace std;

int Graph[110];//地形的状态压缩数
int cur_status[65];//因为同一行的大炮不能相互射击,所以可以把每一行可能的状态列举出来,cur_status 表示一行的所有状态
int cur_status_num[65];//表示对应状态的大炮个数,cur_status_num 是 cur_status 的大小,即总的状态个数
int dp[110][65][65];//dp的值记录当前状态的大炮个数

int MAX(int a, int b)
{
	return a > b ? a : b;
}



int main()
{
	int T;
	char tmp;
	int i, j, k, l;
	int row, col;
	cin>>T;
	while (T--)
	{
		memset(Graph, 0, sizeof(Graph));
		memset(cur_status, 0, sizeof(cur_status));
		memset(cur_status_num, 0, sizeof(cur_status_num));
		memset(dp, 0, sizeof(dp));
		cin>>row>>col;
		if (row == 0)
		{
			cout<<"0"<>tmp;
				if(tmp == 'H')
					Graph[i] += (1 << j);
			}
		}

		//同地图状态压缩,对排列阵型的状态进行压缩,并算出相应阵型的数量。
        //如PHPP有0001 0010 1000 1001 摆法,相应的压缩为 1 2 6 7 相应的炮数为 1 1 1 2
		int s_sum = 0;
		for (i = 0; i < (1 << col); ++i)//初始时找出所有的可能的(合法的)状态,在一个n*m的炮兵地形下每行最多有1<>1))
				cur_status_num[s_sum] += num%2;
			cur_status[s_sum++] = i;
		}
		//动态规划状态转移方程:
        //dp[i][j][k] = max{dp[i-1][k][l]+cur_status[j]},
        //dp[i][j][k]表示第i行状态为cur_status[j],第i-1行状态为cur_status[k]的最大炮兵数
        //枚举l的每种状态,且cur_status[j],cur_status[k],cur_status[l],地形互不冲突
		//第一行炮兵的放置情况
		for (i = 0; i < s_sum; ++i)
		{
			if(cur_status[i] & Graph[0])
				continue ;
			dp[0][i][0] = cur_status_num[i];
		}

		//第二行炮兵放置情况
		for (i = 0; i < s_sum; ++i)//在第一行的所有合法状态中,找出满足与地形相对应的状态
		{
			if(cur_status[i] & Graph[1])
				continue ;
			for (j = 0; j < s_sum; ++j)//在第0行找出与第1行满足条件的状态
			{
				if(cur_status[j] & Graph[0])
					continue ;
				if(cur_status[i] & cur_status[j])
					continue ;
				dp[1][i][j] = MAX(dp[1][i][j], dp[0][j][0] + cur_status_num[i]);
			}
		}

		for (i = 2; i < row; ++i)//找出第i行满足条件的状态
		{
			for (j = 0; j < s_sum; ++j)//在所有第i行合法状态中,找出满足本题条件的状态
			{
				if(cur_status[j] & Graph[i])//查看第i行的地形与预测的合法状态,是否满足条件,是否在高地放置了大炮
					continue ;
				for (k = 0; k < s_sum; ++k)//找出第i-1行与第i行没有冲突的状态
				{
					if(cur_status[k] & Graph[i-1])//找出第i-1行不与地形相矛盾的状态
						continue ;
					if(cur_status[j] & cur_status[k])//第i行的状态状态不与第i-1行状态相矛盾
						continue ;
					for (l = 0; l < s_sum; ++l)//找出不与第i-2行相冲突的状态
					{
						if(cur_status[l] & Graph[i-2])//找出第i-2行所预测的合法状态不与地形相冲突。
							continue ;
						if((cur_status[k] & cur_status[l]) || (cur_status[j] & cur_status[l]))//找出满足条件第i-1行状态不与第i-2行状态相冲突的状态
							continue ;
						dp[i][j][k] = MAX(dp[i][j][k], dp[i-1][k][l] + cur_status_num[j]);
					}
				}
			}
		}

		int MAX_NUM = 0;
		for (i = 0; i < s_sum; ++i)
		{
			for (j = 0; j < s_sum; ++j)
			{
				if(dp[row-1][i][j] > MAX_NUM)
					MAX_NUM = dp[row-1][i][j];
			}
		}
		cout<

 

#include 
#include 
#include 

using namespace std;

int Graph[110];//地形的状态压缩数
int cur_status[65];//因为同一行的大炮不能相互射击,所以可以把每一行可能的状态列举出来,cur_status 表示一行的所有状态
int cur_status_num[65];//表示对应状态的大炮个数,cur_status_num 是 cur_status 的大小,即总的状态个数
int dp[110][65][65];//dp的值记录当前状态的大炮个数

int MAX(int a, int b)
{
	return a > b ? a : b;
}



int main()
{
	int T;
	char tmp;
	int i, j, k, l;
	int row, col;
	cin>>T;
	while (T--)
	{
		memset(Graph, 0, sizeof(Graph));
		memset(cur_status, 0, sizeof(cur_status));
		memset(cur_status_num, 0, sizeof(cur_status_num));
		memset(dp, 0, sizeof(dp));
		cin>>row>>col;
		if (row == 0)
		{
			cout<<"0"<>tmp;
				if(tmp == 'H')
					Graph[i] += (1 << j);
			}
		}

		//同地图状态压缩,对排列阵型的状态进行压缩,并算出相应阵型的数量。
        //如PHPP有0001 0010 1000 1001 摆法,相应的压缩为 1 2 6 7 相应的炮数为 1 1 1 2
		int s_sum = 0;
		for (i = 0; i < (1 << col); ++i)//初始时找出所有的可能的(合法的)状态,在一个n*m的炮兵地形下每行最多有1<>1))
				cur_status_num[s_sum] += num%2;
			cur_status[s_sum++] = i;
		}
		int MAX_NUM = 0;
		//动态规划状态转移方程:
        //dp[i][j][k] = max{dp[i-1][k][l]+cur_status[j]},
        //dp[i][j][k]表示第i行状态为cur_status[j],第i-1行状态为cur_status[k]的最大炮兵数
        //枚举l的每种状态,且cur_status[j],cur_status[k],cur_status[l],地形互不冲突
		//第一行炮兵的放置情况
		for (i = 0; i < s_sum; ++i)
		{
			if(cur_status[i] & Graph[0])
				continue ;
			dp[0][i][0] = cur_status_num[i];
			if(dp[0][i][0] > MAX_NUM)
                MAX_NUM = dp[0][i][0];
		}

		//第二行炮兵放置情况
		for (i = 0; i < s_sum; ++i)//在第一行的所有合法状态中,找出满足与地形相对应的状态
		{
			if(cur_status[i] & Graph[1])
				continue ;
			for (j = 0; j < s_sum; ++j)//在第0行找出与第1行满足条件的状态
			{
				if(cur_status[j] & Graph[0])
					continue ;
				if(cur_status[i] & cur_status[j])
					continue ;
				dp[1][i][j] = MAX(dp[1][i][j], dp[0][j][0] + cur_status_num[i]);
				if(dp[1][i][j] > MAX_NUM)
                    MAX_NUM = dp[1][i][j];
			}
		}

		for (i = 2; i < row; ++i)//找出第i行满足条件的状态
		{
			for (j = 0; j < s_sum; ++j)//在所有第i行合法状态中,找出满足本题条件的状态
			{
				if(cur_status[j] & Graph[i])//查看第i行的地形与预测的合法状态,是否满足条件,是否在高地放置了大炮
					continue ;
				for (k = 0; k < s_sum; ++k)//找出第i-1行与第i行没有冲突的状态
				{
					if(cur_status[k] & Graph[i-1])//找出第i-1行不与地形相矛盾的状态
						continue ;
					if(cur_status[j] & cur_status[k])//第i行的状态状态不与第i-1行状态相矛盾
						continue ;
					for (l = 0; l < s_sum; ++l)//找出不与第i-2行相冲突的状态
					{
						if(cur_status[l] & Graph[i-2])//找出第i-2行所预测的合法状态不与地形相冲突。
							continue ;
						if((cur_status[k] & cur_status[l]) || (cur_status[j] & cur_status[l]))//找出满足条件第i-1行状态不与第i-2行状态相冲突的状态
							continue ;
						dp[i][j][k] = MAX(dp[i][j][k], dp[i-1][k][l] + cur_status_num[j]);
						if(dp[i][j][k] > MAX_NUM)
                            MAX_NUM = dp[i][j][k];
					}
				}
			}
		}
		/**
		int MAX_NUM = 0;
		for (i = 0; i < s_sum; ++i)
		{
			for (j = 0; j < s_sum; ++j)
			{
				if(dp[row-1][i][j] > MAX_NUM)
					MAX_NUM = dp[row-1][i][j];
			}
		}
		*/
		cout<

 

 

 

 

 

你可能感兴趣的:(Nyoj,DP,ACM)