HDU4539---郑厂长系列故事——排兵布阵状压DP

本文介绍如何使用状态压缩动态规划(状压DP)解决一个关于士兵布阵的算法问题,考虑到士兵的攻击范围和地形限制,通过实例解析算法实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4539

Problem Description

郑厂长不是正厂长
  也不是副厂长
  他根本就不是厂长
  事实上
  他是带兵打仗的团长

一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
  根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
  现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。

Input

输入包含多组测试数据;
每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;
接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。

Output

请为每组数据计算并输出最多能安排的士兵数量,每组数据输出一行。

Sample Input
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

Sample Output
2


emmm,和POJ的1185炮兵阵地一样的解法,具体解析请看我的状压DP(2):
https://blog.youkuaiyun.com/qq_43906000/article/details/90905180

但稍微有点不同的就是加了个曼哈顿距离,其实也就是走两步能够达到的,比如:
1 0 是放不了的,1 0 1也是放不了的。那么我们只需要在炮兵阵营的基础上删掉
0 1
一步的情况,然后加个对角线的情况就好了:

if (((v[j]<<1)&v[k]) || ((v[j]>>1)&v[k]))

这个就是对角线的情况了。。。没什么难的。。。其他的就和炮兵阵地一样了。。。

那么这题也就OK了,只不过要注意多组输入。。我就是多组的问题WA了一次又T了一次。。。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;
#define fr(i,n) for (int i=0; i<n; i++)
int a[105][12],v[172],num[172],ans=0;
int cnt=0,n,m,dp[105][172][172];
int ok(int r,int s);
int main() {
	while (scanf ("%d%d",&n,&m)!=EOF) {
		cnt=0;ans=0;
		memset(dp,0,sizeof(dp));
		memset(v,0,sizeof(v));
		memset(num,0,sizeof(num));
		fr(i,(1<<m)) if ((i&(i<<2))==0) {
			v[cnt++]=i;
			int sum=0;
			fr(j,m) if (i&(1<<j)) sum++;
			num[cnt-1]=sum;
		}
		fr(i,n) fr(j,m) scanf ("%d",&a[i][j]);
		fr(i,n) {
			fr(j,cnt) {
				if (!ok(i,v[j])) continue;
				if (i==0) {
					dp[i][j][0]=num[j];
					continue;
				}
				if (i==1) {
					fr(k,cnt) {
						if (((v[j]<<1)&v[k]) || ((v[j]>>1)&v[k])) continue;
						dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][0]+num[j]);
					}
					continue;
				}
				fr(k,cnt) {
					if (((v[j]<<1)&v[k]) || ((v[j]>>1)&v[k])) continue;
					fr(g,cnt) {
						if (v[j]&v[g]) continue;
						dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][g]+num[j]);
					}
				}
			}
		}
		fr(i,cnt) fr(j,cnt) ans=max(ans,dp[n-1][i][j]);
		printf ("%d\n",ans);
	}
	return 0;
}
int ok(int r,int s) {
	fr(i,m) if ((s&(1<<i)) && !a[r][i]) return 0;
	return 1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值