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

2024-05-10 16:32

本文主要是介绍状态压缩(1) Hdu 4539 郑厂长系列故事——排兵布阵,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接: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


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


Source
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[N][M],ok[N];//ok[i]存原图第i行的二进制数
int p2[M],all[N][200],ind[N];//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 0puts("");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[N][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 linenstate = 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 linenstate = 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-2for (int k = 0; k < ind[x]; k++){   mstate = all[x][k];if(pstate & mstate)continue;//x <-> x-2if((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 0puts("");for(int i=1;i<=n;i++)printf("%d ",ok[i]);
#endif	done();printf("%d\n",solve());}return 0;
}




这篇关于状态压缩(1) Hdu 4539 郑厂长系列故事——排兵布阵的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/976972

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while