炮兵阵地(状态压缩dp)

炮兵阵地

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 6
描述
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入
第一行输出数据测试组数X(0<X<100)
接下来每组测试数据的第一行包含两个由空格分割开的正整数,分别表示N和M; 接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。0<=N <= 100;0<=M <= 10。
输出
每组测试数据输出仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
样例输入
1
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
样例输出
6


状态压缩问题(不大懂,学习一下)

思路方法来自点击打开链接


首先分析一下这个题目:

对于某一点(i,j)放置与不放置炮兵问题,相关的点是mp[i][j](平原和山地不同),还有就是i-1的放置情况和i-2的放置情况

而i-1和i-2行的情况影响着下一行放置与不放置的问题和放置多少的问题。

1.状态压缩:

    每一个位置两种状态,即可以用01来表示,即可以表示成一个数字(二进制转化为十进制)常用灵活转化

    每个位置放置与不放置也是两种状态可以用01表示最后转化为一个十进制的数字

2.如何多层for去对应一个个点复杂度太高,而通过状态压缩后的数字来进行对比。(&运算,逐位比较)

    for循环十进制数字转化为二进制,判断:

        (1)左右一位和两位没有炮兵  (2)该地方为平原

3.首先我们单纯的寻找在所有的点都是平原的情况下的可行解

4.再把可行解与地形情况作对比,与运算寻找&值0&1==0 (平原可放), 1 & 0 == 0(高地不可放),0&0 ==0(平原不放),都是可行的。1&1 == 1,不可行

4.第i行的放置状态不可以与i-1行和i-2行冲突

推出对应的递推式子是

dp[i][j][k] = dp[i-1][k][l] + state_num[j]与当前值的最大

i表示第i行,j表示i行的当前状态,k表示i-1行的状态,l表示i-2的当前状态,state_num[j]表示当时j状态所放置的炮兵数


重点,先找状态压缩后的可行解,然后与限制条件对比,找出符合的情况,求递推公式。

  

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const int M = 11;

int state[N];
int state_num[N];
int mp[N];
int dp[N][N][N];
int sum_s;

bool cmp(int a, int b)
{
	if((a&b) == 0) return true; // 此处注意 == 的优先级高于&运算 
	else return false;
}

void find_state(int m)//寻找放置炮兵的可行解
{
	sum_s = 0;
	for(int i = 0; i < (1<<m); i++ )
	{
		if((i&(i<<1))==0 && (i&(i<<2))==0) //先随左边放 
		{
			//cout << sum_s <<endl;
			state[sum_s] = i;
			state_num[sum_s] = 0;
			for(int j = 1; j <= state[sum_s]; j=(j<<1))
			{
				if(j&state[sum_s]) state_num[sum_s]++;//判断第i位(倒数)是否为1,也就是求放置炮兵的总数 
			}
			sum_s ++;
		}
	}
 } 

int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		memset(dp, 0, sizeof(dp));
		memset(mp, 0, sizeof(mp));
		memset(state, 0, sizeof(state));
		memset(state_num, 0, sizeof(state_num)); 
		int n, m;
		scanf("%d %d", &n, &m);
		find_state(m);
		//cout << sum_s <<endl;
		if(n == 0) 
		{
			cout << "0" << endl;
			continue;
		}
		
		char c;
		for(int i = 1; i <= n; i++)
		{
			getchar();
			for(int j = 0; j < m; j++)
			{
				cin >> c;
				if(c == 'H') mp[i] += (1<<j);  //这样算出的mp是逆序的,但不影响最终结果	
			}
			//cout << mp[i] << endl;
		}
		
		int ans = -1;
		for(int i = 0; i < sum_s; i++)
		{
			//cout << mp[1] << " " << state[i] <<" "<< (mp[1] & state[i]) << endl;
			if(cmp(mp[1], state[i]))//合适 
			{
				//cout << "enable" << i << endl;
				dp[1][i][0] = max(dp[1][i][0], state_num[i]);
				if(n == 1) ans = max(dp[1][i][0], ans); 
			}
		 } 
		 if(n >= 2)
		 {
		 	for(int i = 0; i < sum_s; i++)
		 	{
		 		if(!cmp(mp[2], state[i])) continue;
		 		for(int j = 0; j < sum_s; j++)
		 		{
		 			if(!cmp(state[i], state[j])) continue;
		 			dp[2][i][j] = max(dp[2][i][j], dp[1][j][0]+state_num[i]);
		 			if(n == 2) ans = max(ans, dp[2][i][j]);
				 }
			 }
		 }
		 
		 if(n > 2)
		 {
		 	for(int k = 3; k <= n; k++)
		 	{
		 		for(int i = 0; i < sum_s; i++) //第k行 
		 		{
		 			if(!cmp(mp[k], state[i])) continue;
		 			for(int j = 0; j < sum_s; j++) //k-1行 
		 			{
		 				if(!cmp(state[i], state[j])) continue;
		 				for(int l = 0; l < sum_s; l++)
						 {
						 	if(!cmp(state[i], state[l])) continue;
						 	if(!cmp(state[j], state[l])) continue;
						 	dp[k][i][j] = max(dp[k][i][j], dp[k-1][j][l] + state_num[i]);
						 	if(k == n) ans = max(dp[k][i][j], ans);
						  } 
				 	}
				 }
			 }
		 }
		 cout << ans << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yinghui_yht/article/details/79685035