您的位置:首页 > 其它

状态压缩(1) Hdu 4539 郑厂长系列故事——排兵布阵

2014-02-19 17:13 453 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539 

郑厂长系列故事——排兵布阵

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)

Total Submission(s): 1435    Accepted Submission(s): 520


[align=left]Problem Description[/align]
  郑厂长不是正厂长

  也不是副厂长

  他根本就不是厂长

  事实上

  他是带兵打仗的团长

  一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。

  根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。

  现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。

 

[align=left]Input[/align]
输入包含多组测试数据;

每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;

接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。

 

[align=left]Output[/align]
请为每组数据计算并输出最多能安排的士兵数量,每组数据输出一行。

 

[align=left]Sample Input[/align]

6 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

 

[align=left]Sample Output[/align]

2

 

[align=left]Source[/align]
2013腾讯编程马拉松复赛第二场(3月30日)
 

真正意义上的第一道状态压缩题,做完这一道,再也不会对状压题发憷了o(* ̄▽ ̄*)ゞ 。

一般情况的状态压缩题 都会有一个维度很小(小于20),就提示用状态压缩,用二进制数去表示一个状态,用二进制数的操作满足题意的限制条件。

对于一般的状态压缩题,枚举出所有的状态,通过不断判断都可以 dp得到最值。不过这样的时间复杂度 过高,大多数题都不能过,于是就可以根据题意的限制条件来预处理每种状态的合理性,保存下较少的合理状态,就可以用较短的时间得到结果。

分析本题:给一个n*m的矩阵,每个位置用来放置士兵,0表示不可以,1表示不可以。而且一个士兵不能站在另一个士兵的攻击点上。

注意到一个维度 m 的范围是小于等于10,于是我们就用一个10位的二进制数来表示一行的状态,用二进制中的0和1来表示是否占用其位置。

再看士兵的攻击范围:是距离当前士兵的曼哈顿距离为2的周围8个点(当然不能超出地图)。发现当前点是否可以放士兵 是跟前两行都是有关系的,所以用三重循环来判断状态是否可行。

注意要 预处理 每行的状态(简单判断一下):根据原有地图的可放置与不可放置点,还有在同一行的曼哈顿距离为2 的点。。来进行初步的筛选状态,并存储起来,用其数组下标表示状态,可以减少复杂度。

具体看代码:

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 105
#define M 13
int n,m;
int map
[M],ok
;//ok[i]存原图第i行的二进制数
int p2[M],all
[200],ind
;//p2存2的指数幂,all[i][j]存第i行可以成立的第j种状态的二进制数.ind[i]存第i行可以成立的状态数。
void init(){
p2[0] = 1;
for (int i = 1; i < 12; i++)
p2[i] = p2[i-1]*2;
return ;
}
void done(){
memset(ind,0,sizeof(ind));
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= ok[i]; j++)
{
if((j | ok[i]) > ok[i])continue;
if((j & (j << 2) )!= 0)continue;
if((j & (j >> 2) )!= 0)continue;
all[i][ind[i]++] = j;
}
}
#if 0
puts("");
for (int i = 1; i <= n; i++)
{
printf("%d\n",ind[i]);
for (int j = 0; j < ind[i]; j++)
{
printf("%d ",all[i][j]);
}puts("");
}
for (int i = 0; i < 12; i++)
{
printf("%d ",p2[i]);
}puts("");
#endif
}
int cal_num(int x){
int num=0;
while(x){
x = x&(x-1);
num++;
}return num;
}
int dp
[200][200];//dp[x][i][j] 当第x行是i状态,x-1行是j状态时 从1->x行能够放置的炮兵的最大值
int solve()
{
int ans = 0;
memset(dp,0,sizeof(dp));
int nstate,mstate;
for (int j = 0; j < ind[1]; j++){//init the first line
nstate = all[1][j];
dp[1][j][0] = cal_num(nstate);
ans = max(ans,dp[1][j][0]);
}
for (int i = 0; i < ind[1]; i++){//init the second line
nstate = all[1][i];
for (int j = 0; j < ind[2]; j++)
{
mstate = all[2][j];
if((mstate & (nstate>>1)) || (nstate & (mstate>>1)))continue;//2 <-> 1
//if((mstate & (nstate<<1)) || (nstate & (mstate<<1)))continue;
dp[2][j][i] = dp[1][i][0] + cal_num(mstate);
ans = max(ans,dp[2][j][i]);
}
}
int pstate;
for (int x = 3; x <= n; x++)//deal with the 3rd to nth line
{
for (int i = 0; i < ind[x-2]; i++)
{ pstate = all[x-2][i];
for (int j = 0; j < ind[x-1]; j++)
{ nstate = all[x-1][j];
if((nstate & (pstate>>1)) || (pstate & (nstate>>1)))continue;//x-1 <-> x-2
//if((pstate & (nstate<<1)) || (nstate & (pstate<<1)))continue;//x-1 <-> x-2
for (int k = 0; k < ind[x]; k++)
{ mstate = all[x][k];
if(pstate & mstate)continue;//x <-> x-2
if((mstate & (nstate>>1)) || (nstate & (mstate>>1)))continue;//x <-> x-1
// if((mstate & (nstate<<1)) || (nstate & (mstate<<1)))continue;
dp[x][k][j] = max(dp[x-1][j][i]+cal_num(mstate),dp[x][k][j]);
ans = max(ans,dp[x][k][j]);
}
}
}
}
return ans;
}
int main()
{
init();
while(scanf("%d%d",&n,&m)!=EOF){
memset(ok,0,sizeof(ok));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
scanf("%d",&map[i][j]);
ok[i] += map[i][j] * p2[j-1];
}

#if 0
puts("");
for(int i=1;i<=n;i++)
printf("%d ",ok[i]);
#endif
done();
printf("%d\n",solve());
}
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  状态压缩