洛谷 P2337 [SCOI2012] 喵星人的入侵

题目描述

a180285 幸运地被选作了地球到喵星球的留学生,其实是作为特工去调查,喵星人是否有侵略地球的企图。喵星人果然打算入侵地球!从 a180285 口中得到确切消息之后,地球小组成员决定制定反侵略计划。

喵星到地球的一段必经之路可以看作 �×�n×m 的格点,喵星人将会从地图上的 �S 位置出发,目的地是地球的入口 �T。为了抵抗喵星人的入侵,地球防御小组打算在地图上的格点上放置一些炮塔(最多放置 �K 个),炮塔攻击周围的 88 个方向(88 个方向分别是:东、南、西、北、东北、西北、东南、西南)(如下图所示,中间格子的炮塔可以攻击周围的八个格子)。此外地球防御小组还可以在地图上放置无限多个障碍,使得喵星人无法从有障碍的格子经过。

右图是 3×33×3 地图的一个示例,其中 �X 表示炮塔,## 表示障碍,有炮塔或者障碍的格子,喵星人都无法经过。在这张地图中喵星人从 �S 走到 �T 受到的伤害如下:

  • 在 �(1,0)S(1,0) 处受到伤害为 22(炮塔 (0,0)(0,0) 和 (2,1)(2,1) 能攻击到 �S);
  • 在空地 (1,1)(1,1) 处受到伤害为 33(同时被炮塔 (0,0),(0,2)(0,0),(0,2) 和 (2,1)(2,1) 攻击);
  • 在 �(1,2)T(1,2) 处受到伤害为 22(炮塔 (0,2)(0,2) 和 (2,1)(2,1) 能攻击到 �T)。

于是受到的总伤害为 2+3+2=72+3+2=7。

作为地球防御小组的成员,请你为喵星人布阵,使得喵星人受到的伤害最大。注意如果有多条从 �S 到 �T 的路径,喵星人会选择伤害最小的一条。

输入格式

第一行为三个整数 �,�,�n,m,K,分别表示地图的长和宽,以及最多能放置的炮塔数量。

接下来的 �n 行,每行包含 �m 个字符,## 表示地图上原有的障碍,.. 表示该处为空地,数据保证在原地图上存在 �S 到 �T 的路径。

输出格式

输出在合理布阵下,喵星人采取最优策略后,会受到的最大伤害。

注意必须保证在布阵结束后喵星人仍然可以沿一条或以上的路径从起点 �S 到达终点 �T,否则他们组织更大规模的侵略。

输入输出样例

输入 #1复制

3 3 1
S.T
...
...

输出 #1复制

7

说明/提示

样例解释

样例的一种最优布局方案如下:

S#T
.X.
...

数据范围及约定

  • 对于 30%30% 的数据,保证 1≤�,�≤61≤n,m≤6;
  • 对于 100%100% 的数据,保证 min⁡(�,�)≤6min(n,m)≤6,max⁡(�,�)≤20max(n,m)≤20,1≤�≤151≤K≤15 且从 �S 到 �T 的路径必定存在。
  • 这题呢,看一眼数据范围,大概是插头dp

    那我们就要考虑如何设计状态了

    显然,本题中只记录轮廓线的插头状态是不够的,因为每个点的权值是由它周围的八个点的状态决定的,所以我们还要记录轮廓线的路径,也就是轮廓线上是放障碍还是炮台还是路径

    我们记轮廓线上如果是障碍则为状态 0,如果是路径则为状态 1,如果是炮台则为 3

    当然,我们发现插头状态仅记录左括号(记为1),右括号(记为2),和无插头(记为0)是不够的,因为本题中可能有没有括号和它匹配的插头,我们称其为独立插头(记为3)

    我们又发现炮台的数量还有个限制,于是还要加一个状态记录炮台的数量

    接下来,就是喜闻乐见的大力分类讨论时间啦

    (当然,如果你没有做过这个题,那么建议你先去做一下,因为本题插头使用括号表示法)

    我们记 ��1pl1 为右插头,记 ��2pl2 为下插头

  • 这个点 ��1=0pl1=0 且左边是路径或 ��2=0pl2=0 且上边是路径
    这种情况显然需要用障碍去堵住
    那么,显然只有无左插头且无下插头的情况是合法的
    接下来,只要转移这个点放炮台或障碍就行了

  • 这个点是障碍点
    这应该是最简单的一种
    显然只有无左插头且无下插头的情况是合法的
    直接转移即可

  • 这个点是非障碍点
    (疯狂的地方开始了)
    1)若 ��1=0pl1=0 且 ��2=0pl2=0
     则可以添加炮台,或者新建一个联通分量,或者不走
    2)若 ��1=0pl1=0 或 ��2=0pl2=0
     则直接延伸插头即可
    3)若 ��1=1pl1=1 且 ��2=1pl2=1
     则需要向右寻找一个右括号换成左括号并删除当前插头
    4)若 ��1=2pl1=2 且 ��2=2pl2=2
     则需要向左寻找一个左括号换成右括号并删除当前插头
    5)若 ��1=1pl1=1 且 ��2=2pl2=2
     不合法,因为这样会形成闭合回路
    6)若 ��1=2pl1=2 且 ��2=1pl2=1
     直接删除插头即可
    7)若 ��1=1pl1=1 且 ��2=3pl2=3 或 ��1=3pl1=3 且 ��2=1pl2=1
     则需要向右寻找一个右括号替换成独立插头并删除当前插头
    8)若 ��1=2pl1=2 且 ��2=3pl2=3 或 ��1=3pl1=3 且 ��2=2pl2=2
     则需要向左寻找一个左括号替换成独立插头并删除当前插头
    9)若 ��1=3pl1=3 且 ��2=3pl2=3
     直接删除插头即可

  • 这个点是起点或者终点
    1)若 ��1=0pl1=0 且 ��2=0pl2=0
     则需要新建一个独立插头
    2)若 ��1=1pl1=1 且 ��2=0pl2=0 或 ��1=0pl1=0 且 ��2=1pl2=1
     则需要向右寻找一个右括号替换成独立插头并删除当前插头
    3)若 ��1=2pl1=2 且 ��2=0pl2=0 或 ��1=0pl1=0 且 ��2=2pl2=2
     则需要向左寻找一个左括号替换成独立插头并删除当前插头
    4)若 ��1=3pl1=3 且 ��2=0pl2=0 或 ��1=0pl1=0 且 ��2=3pl2=3
     直接删除插头即可

  • 代码如下:

#include<bits/stdc++.h>
#define MAXN 25
#define p 100007
#define int long long
using namespace std;
int n,m,K,now,pre;
char c[MAXN][MAXN];
int head[p+3],nxt[1<<24];
int f[2][1<<24],fst[2][3][1<<24];
int cnt[2];
void insert(int state1,int state2,int state3,int val)
{
	int ha=((((1ll*state1<<16)|state2)<<4)|state3)%p+1;//哈希
	for(int i=head[ha];i;i=nxt[i])
		if(fst[now][0][i]==state1&&fst[now][1][i]==state2&&fst[now][2][i]==state3)
		{
			f[now][i]=max(f[now][i],val);
			return;
		}
	f[now][++cnt[now]]=val;
	fst[now][0][cnt[now]]=state1;
	fst[now][1][cnt[now]]=state2;
	fst[now][2][cnt[now]]=state3;//三个状态
	nxt[cnt[now]]=head[ha];
	head[ha]=cnt[now];
}//hash表
int left(int state,int pos,int val)
{//向左寻找左括号
	int tot=1;
	while(true)
	{
		int pl=(state>>(pos<<1))&3;
		if(pl==2)tot++;if(pl==1) tot--;
		if(!tot)return state^(pl<<(pos<<1))^(val<<(pos<<1));
		pos--;
	}
}
int right(int state,int pos,int val)
{//向右寻找右括号
	int tot=1;
	while(true)
	{
		int pl=(state>>(pos<<1))&3;
		if(pl==1)tot++;if(pl==2) tot--;
		if(!tot)return state^(pl<<(pos<<1))^(val<<(pos<<1));
		pos++;
	}
}
char tmp[MAXN][MAXN];
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&K);
	for(int i=0;i<n;i++)scanf("%s",c[i]);
	if(n<m)
	{//n<m的时候把地图转90度
		memcpy(tmp,c,sizeof(c));
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)
				c[j][i]=tmp[i][j];
		swap(n,m);
	}
	cnt[0]=1;
	for(int i=0;i<n;i++)
	{
		for(int j=1;j<=cnt[now];j++)
			fst[now][0][j]<<=2;
		for(int j=1;j<=cnt[now];j++)
			fst[now][1][j]=(fst[now][1][j]^(((fst[now][1][j]>>(m+1<<1))&3)<<(m+1<<1)))<<2;//更新上一行的状态
		for(int j=0;j<m;j++)
		{
			pre=now;now^=1;cnt[now]=0;
			memset(head,0,sizeof(head));//注意初始化
			for(int k=1;k<=cnt[pre];k++)
			{
				int sta=fst[pre][0][k],stb=fst[pre][1][k],stc=fst[pre][2][k];
				int val=f[pre][k];
				if(sta>=(1<<(m+1<<1)))continue;
				int pl1=(sta>>(j<<1))&3;//right
				int pl2=(sta>>(j+1<<1))&3;//down
				int r=(stb>>(j<<1))&3;//右状态
				int ur=(stb>>(j+1<<1))&3;//右上状态
				int u=(stb>>(j+2<<1))&3;//上状态
				int ul=(stb>>(j+3<<1))&3;//左上状态
				int now=(sta^(pl1<<(j<<1))^(pl2<<(j+1<<1)));
				if((!pl1&&r==1)||(!pl2&&u==1))//1
				{
					if(pl1||pl2)continue;
					insert(now,stb^(ur<<(j+1<<1)),stc,val);//放障碍
					if(stc<K&&c[i][j]=='.')
						insert(now,stb^(ur<<(j+1<<1))^(3<<(j+1<<1)),stc+1,val+(r==1)+(ur==1)+(u==1)+(ul==1));//放炮台
				}
				else if(c[i][j]=='#'){if(!pl1&&!pl2)insert(now,stb^(ur<<(j+1<<1)),stc,val);}//2
				else if(c[i][j]=='.')//3
				{
					if(!pl1&&!pl2)//1)
					{
						if(stc<K)
							insert(now,stb^(ur<<(j+1<<1))^(3<<(j+1<<1)),stc+1,
								val+(r==1)+(ur==1)+(u==1)+(ul==1));//放炮台
						insert(now^(1<<(j<<1))^(2<<(j+1<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));//走
						insert(now,stb^(ur<<(j+1<<1)),stc,val);//不走
					}
					else if(!pl1&&pl2)//2)
					{
						insert(now^(pl2<<(j<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
						insert(now^(pl2<<(j+1<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					}
					else if(pl1&&!pl2)//2)
					{
						insert(now^(pl1<<(j<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
						insert(now^(pl1<<(j+1<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					}
					else if(pl1==1&&pl2==1)//3)
						insert(right(now,j,1),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==2&&pl2==2)//4)
						insert(left(now,j,2),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==2&&pl2==1)//6)
						insert(now,stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==1&&pl2==3)//7)
						insert(right(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==2&&pl2==3)//8)
						insert(left(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==3&&pl2==1)//7)
						insert(right(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==3&&pl2==2)//8)
						insert(left(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==3&&pl2==3)//9)
						insert(now,stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
				}
				else if(c[i][j]=='S'||c[i][j]=='T')//4
				{
					if(c[i][j]=='S'&&!pl1&&!pl2)//1)
					{
						insert(now^(3<<(j<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
						insert(now^(3<<(j+1<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					}
					else if(c[i][j]=='T'&&!pl1&&!pl2)//1)
					{
						insert(now^(3<<(j<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
						insert(now^(3<<(j+1<<1)),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					}
					else if(!pl1&&pl2==1)//2)
						insert(right(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(!pl1&&pl2==2)//3)
						insert(left(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==1&&!pl2)//2)
						insert(right(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==2&&!pl2)//3)
						insert(left(now,j,3),stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(pl1==3&&!pl2)//4)
						insert(now,stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
					else if(!pl1&&pl2==3)//4)
						insert(now,stb^(ur<<(j+1<<1))^(1<<(j+1<<1)),stc,
							val+(r==3)+(ur==3)+(u==3)+(ul==3));
				}
			}
		}
	}
	int ans=0;
	for(int i=1;i<=cnt[now];i++)//统计答案
		if(!fst[now][0][i])
			ans=max(ans,f[now][i]);
	printf("%lld",ans);
	return 0;
}

拜拜! 

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值