洛谷 P2337 【[SCOI2012]喵星人的入侵】
这几天一直在刷插头Dp,写了几道入门题后,觉得还比较水,直到我发现了这一题、、、、
题目大意:给你一个n*m的地图,有些是空地,有些是障碍,还有两个是ST,在给你一个L,代表可以放L个炮台,你要在空地上放炮台或者障碍,来使得S到T存在路径,喵星人会选择伤害最小的一条路径来走,你需要输出喵星人受到的最大伤害 (伤害就是指你被炮台打到的次数,炮台可以打上下左右还有斜的)
首先,很显然最后只会剩一条路径,否则就用障碍堵住就OK了
然后就变出裸裸的插头Dp了,f[k][i][j][s1][s2]代表用了k个炮台,在(i,j)这个位置,插头状态为s1,种类状态为s2,的最大伤害。 显然大多数的s2可以直接推出s1,所以状态数并不多。
因为会MLE,所以第一维要开滚动数组,具体的话看代码。
code:
//开o2
#include<cstdio>
#include<cstring>
#include<cassert>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=,maxm=;
int n,m,L,now,last,ans; char map[maxm][maxm]; bool xxx[maxm][maxm];
void init(){
scanf("%d%d%d",&n,&m,&L);
for (int i=;i<=n;++i) scanf("%s",map[i]+);
if (n<m){
for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (!xxx[i][j]){
xxx[i][j]=xxx[j][i]=;
swap(map[i][j],map[j][i]);
}
swap(n,m);
}
for (int i=;i<=n;++i) for (int j=;j<=m;++j) if (map[i][j]=='T') map[i][j]='S';
}
struct Tp{int s1,s2,v;};
struct Tsp{
static const int mod=,maxm=,Index=(int)(1e9+);
int now[mod],pre[maxm],tot; Tp f[maxm];
void clear(){memset(now,,sizeof(now)); tot=;}
int find(Tp a){
int tmp=(1LL*a.s1*Index+a.s2)%mod;
for (int p=now[tmp];p;p=pre[p]) if (f[p].s1==a.s1 && f[p].s2==a.s2){
if (a.v>f[p].v) f[p].v=a.v; return f[p].v;
}
pre[++tot]=now[tmp]; now[tmp]=tot;
f[tot].s1=a.s1; f[tot].s2=a.s2; return f[tot].v=a.v;
}
}f[][][];
void addstate(int x,int y,Tp a){ // 插入进f[now][x][y]
f[now][x][y].find(a);
}
int cont1[maxn],cont2[maxn]; // cont1代表s1,cont2代表s2
// s1维护插头状态 0,1,2,3分别表示没插头,左插头,右插头,独立插头 s1还要多维护一个插头
// s2维护种类 0,1,2分别表示障碍,路径,炮台 s2还要多维护一个种类
void decode(Tp a){
for (int i=m+;i>=;--i) cont1[i]=a.s1&,a.s1>>=;
for (int i=m+;i>=;--i) cont2[i]=a.s2&,a.s2>>=;
}
Tp encode(int v){
Tp a; a.v=v; a.s1=; a.s2=;
for (int i=;i<=m+;++i) a.s1=(a.s1<<)+cont1[i];
for (int i=;i<=m+;++i) a.s2=(a.s2<<)+cont2[i];
return a;
}
int findr(int res,int x){
for (;;++x) if (cont1[x]== || cont1[x]==){
if (cont1[x]==) ++res; else --res;
if (!res) return x;
}
}
int findl(int res,int x){
for (;;--x) if (cont1[x]== || cont1[x]==){
if (cont1[x]==) ++res; else --res;
if (!res) return x;
}
}
int find(int a,int x){
if (a==) return findr(,x+);
return findl(-,x-);
}
void expand(int x,int y,Tp a){ // 不能放炮台的转移
decode(a);
int north=cont1[y],west=cont1[m+],nw=cont2[m+];
if (y== && west) return ; if (y==) nw=; cont2[m+]=cont2[y]; // 更新左上角的种类 int sum=; for (int i=;i<=m+;++i) if (cont1[i]==) ++sum; // 统计独立插头数量 int ad=; if (y> && cont2[y-]==) ++ad; if (nw==) ++ad;
if (cont2[y]==) ++ad; if (y<m && cont2[y+]==) ++ad; // 统计轮廓线上方八连通的炮台数 if (map[x][y]=='.'){ // 作为一个路径,却与起点没有关系
if ((y== || west || cont2[y-]!=) && (x== || north || cont2[y]!=)){ // 可以更新
cont2[y]=; // 更新s2
if (west && north){ // (1,2) 不可更新,否则成回路了
if (west==north){
if (west==){ // (3,3)
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
else{ // (1,1) (2,2)
cont1[find(west,y)]=west; cont1[y]=; cont1[m+]=;
addstate(x,y,encode(a.v+ad));
}
}
else if (west== || north==){ // (1,3) (2,3) (3,1) (3,2)
cont1[find(min(west,north),y)]=; cont1[y]=; cont1[m+]=;
addstate(x,y,encode(a.v+ad));
}
else if (west== && north==){ // (2,1)
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
}
else if (west && !north){ // 只能转向,不能升级成独立
cont1[y]=; cont1[m+]=west; addstate(x,y,encode(a.v+ad));
cont1[y]=west; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
else if (!west && north){ // 同上
cont1[y]=; cont1[m+]=north; addstate(x,y,encode(a.v+ad));
cont1[y]=north; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
else if (!west && !north){ // 可以变出两个左右插头,但是不能变成独立
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
}
} if (map[x][y]=='S'){ // 作为一个起点,专业生产独立插头
if ((y== || west || cont2[y-]!=) && (x== || north || cont2[y]!=)){ // 老规矩
cont2[y]=; // 虽然是起点,但也是路径
if (west && north){ // 可以滚粗了
}
if ((west && !north) || (!west && north)){
if (max(west,north)==){ // 是独立插头,合并
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
else if (sum<=){ // 不是独立插头,将其升级为独立插头
cont1[find(max(west,north),y)]=;
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
}
if (!west && !north && sum<=){ // 没有插头,那就直接产生一个
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v+ad));
}
}
} if (map[x][y]!='S'){ // 不是起点大哥,就可以放障碍啦~
if (!west && !north){ // 当然要没有插头才可以放障碍
if (map[x][y]=='.') decode(a),cont2[m+]=cont2[y];
cont2[y]=; cont1[y]=; cont1[m+]=; addstate(x,y,encode(a.v));
}
}
}
void expand_pt(int x,int y,Tp a){ // 只能放炮台
decode(a);
int north=cont1[y],west=cont1[m+],nw=cont2[m+];
if (north || west || map[x][y]!='.') return; if (y==) nw=; int ad=; if (y> && cont2[y-]==) ++ad; if (nw==) ++ad;
if (cont2[y]==) ++ad; if (y<m && cont2[y+]==) ++ad; // 统计路径个数 cont2[m+]=cont2[y]; cont2[y]=; addstate(x,y,encode(a.v+ad));
}
void clearf(int x){
for (int i=;i<=n;++i) for (int j=;j<=m;++j) f[x][i][j].clear();
}
void work(){
now=,last=; clearf(now); Tsp *x;
for (int l=;l<=L;++l){ // now->l , last->l-1
addstate(,,(Tp){,,}); x=&f[now][][]; // l更新l now->now
for (int i=;i<=n;++i) for (int j=;j<=m;++j){ // x是上一个
for (int k=;k<=x->tot;++k) expand(i,j,x->f[k]);
x=&f[now][i][j];
}
swap(now,last); clearf(now); // now->l+1 , last=l
if (l!=L){
x=&f[last][][];
for (int i=;i<=n;++i) for (int j=;j<=m;++j){
for (int k=;k<=x->tot;++k) expand_pt(i,j,x->f[k]);
x=&f[last][i][j];
}
}
for (int k=;k<=x->tot;++k)
if (x->f[k].s1== && x->f[k].v>ans) ans=x->f[k].v;
}
printf("%d\n",ans);
}
int main(){
init();
work();
return ;
}
洛谷 P2337 【[SCOI2012]喵星人的入侵】的更多相关文章
- 洛谷 P2336 [SCOI2012]喵星球上的点名 解题报告
P2336 [SCOI2012]喵星球上的点名 题目描述 a180285 幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有 \(N\) 个喵星人,每个喵星人的 ...
- 洛谷 P2960 [USACO09OCT]Milkweed的入侵Invasion of the Milkweed
P2960 [USACO09OCT]Milkweed的入侵Invasion of the Milkweed 题目描述 Farmer John has always done his best to k ...
- 洛谷P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)
我学AC自动机的时候就看到了这题,想用AC自动机结果被学长码风劝退-- 学后缀数组时又看到了这题--那就写写后缀数组做法吧 结果码风貌似比当年劝退我的学长还毒瘤啊 对所有的模式串+询问串,不同串之间用 ...
- 洛谷$P5038\ [SCOI2012]$奇怪的游戏 二分+网络流
正解:二分+网络流 解题报告: 传送门$QwQ$ 这种什么,"同时增加",长得就挺网络流的$QwQ$?然后看到问至少加多少次,于是考虑加个二分呗?于是就大体确定了做题方向,用的网络 ...
- 洛谷5038 [SCOI2012]奇怪的游戏(二分+网络流+判断奇偶)
寒假的时候就听过这个题.但是一直没有写. qwq 首先,我们发现题目中的图是个网格图,然后每次可以将相邻两个格子加一. 很容易就想到是黑白染色.那么每次操作,就相当于同时操作一个白点,一个黑点. 我们 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
- 洛谷P1108 低价购买[DP | LIS方案数]
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP
题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...
随机推荐
- 51单片机 小车 L298N pwm调速 串口控制 按键控制
难点:1.串口定时器T1,和T0定时器优先级 2.pwm频率与占空比的设置 按键控制 按键1——前进 按键2——后退 按键3——加速 按键4——减速 (板子上只有四个按键) 串口控制 ‘1’——前进 ...
- 51NOD 1134 最长上升子序列
给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递增的) 例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10. 输入 第1行:1个数N, ...
- Typora练习测试
目录 一级标题 二级标题 三级标题 一级标题 二级标题 三级标题 这是下划线 删除线 字体加粗ctrl+b 这是倾斜线 1111 牛奶 面包 鸡蛋 包子 蛋糕 测试 牛奶 面包 鸡蛋 电脑 鼠标 键盘 ...
- php输出中文字符
中文字符不可以使用imagettftext()函数在图片中直接输出,如果要输出中文字符,需要先使用iconv()函数对中文字符进行编码,语法格式如下:string iconv ( string $in ...
- 143 Reorder List 重排链表
给定一个单链表L:L0→L1→…→Ln-1→Ln,重新排列后为: L0→Ln→L1→Ln-1→L2→Ln-2→…必须在不改变节点的值的情况下进行原地操作.例如,给定链表 {1,2,3,4},按要求重排 ...
- TRUNCATE DELETE DROP 区别
TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行.但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源 ...
- new几种用法
在 C# 中,new 关键字可用作运算符.修饰符或约束. new 运算符 用于创建对象和调用构造函数. new 修饰符 用于向基类成员隐藏继承成员. new 约束 用于在泛型声明中约束可能用作类型参数 ...
- python2和python3的区别(转)
基本语法差异 核心类差异 Python3对Unicode字符的原生支持 Python2中使用 ASCII 码作为默认编码方式导致string有两种类型str和unicode,Python3只支持uni ...
- [BZOJ1040][ZJOI2008]骑士 基环树DP
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1040 题目给出了$n$个点和$n$条无向边,即一棵基环树或者基环树森林. 如果题目给的关系 ...
- 关于获取计算机唯一ID问题
1:CPU序列号,并不是每颗CPU都有一个唯一的序列号,CPU试每种型号一个序列号,其实可以认为是CPU型号号码.PIII以前的计算机没有ID,而且AMD的CPU也没有ID. 创建一个虚拟机,他会重新 ...