解题报告:
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)