Toy Bricks(HZOI模拟试题)

Ray面临一个不规则箱子的NPC问题,箱子内有突起,只能放'十'字形玩具。给定箱子的长宽及内部障碍物分布,求最多能放多少个玩具。输入包括箱子尺寸和障碍物信息,输出是玩具的最大数量。提示中提到问题与炮兵阵地类似,但突起会影响玩具放置,需使用动态规划解决。

Description

Ray又在NPC问题了:这里有一个箱子,还有若干个玩具。 
我们可以假设玩具的数量是没有上限的。我们还知道这个箱子是不规则的,或者可以说,他的外形比较像一个矩形,但是内部却有很多突起,这些突起很可恶,他可以一直突到玩具的顶盖部。为了简化这个NPC问题,Ray只保留了一种形状的玩具

PS:玩具为“十”字形
Ray想知道对于一个给定的箱子,最多可以放下多少个这样的玩具。

Input

第一行两个用空格隔开的整数n 和m表示玩具箱的长度和宽度
第二行到第n+1行,每行m个用空格隔开的字符“#”“.”;“#”表示这里是一个障碍物。“.”表示这里可以放东西。

Output

一行一个整数表示最多的玩具个数

Sample Input

5 4

#.#.

...#

#..#

#...

##.#

Sample Output

2

Hint

0<=n<=100
0<=m<=10

发现这道题和炮兵阵地很像,而且一开始我又陷入了类似炮兵阵地的误区(只开两维,详见炮兵大坑),和炮兵不同的是,迷之突起会相互影响无法兼容,因此我们记下每个凸起位置= =,dp的时候判断一下就可以了。

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
int n,m;
int cannot[110];
int cnts,p[30],st[30],num[30];
inline bool ok(int s){
	int cnt=0;
	for(;s;s>>=1){
		if(!(s&1))if(cnt%3!=0)return 0;
		if(s&1)++cnt;
	}
	if(cnt%3)return 0;
	return 1;
}
inline void cull(int idx,int s){//我们处理出每个状态,玩具数量和...的中间位置(判凸出) 
	int cnt=0,tmps=s;
	for(int i=1;s;++i,s>>=1){
		if(s&1)++cnt;
		if(cnt%3==2)p[idx]|=1<<i-1;
	}
	st[idx]=tmps,num[idx]=cnt/3;
}
int ans,f[110][30][30];
inline void init(){
	scanf("%d%d",&n,&m);
	Inc(i,0,(1<<m)-1)if(ok(i))cull(++cnts,i);

	Inc(i,1,n)Inc(j,1,m){
		char c=getchar();while(c!='#'&&c!='.')c=getchar();
		cannot[i]+=(c=='#')<<j-1;
	}

	Inc(i,1,cnts)if(!(st[i]&cannot[2])&&!(p[i]&cannot[1])&&!(p[i]&cannot[3]))
		Inc(j,1,cnts)if(!(st[j]&cannot[1])&&!(p[j]&cannot[2])&&!(p[j]&st[i]))f[2][i][j]=num[i];
}
inline void solv(){
	Inc(i,3,n-1)Inc(j0,1,cnts)if(!(st[j0]&cannot[i])&&!(p[j0]&cannot[i-1])&&!(p[j0]&cannot[i+1])){
		Inc(j1,1,cnts)if(!(st[j1]&cannot[i-1])&&!(p[j1]&cannot[i-2])&&!(p[j1]&cannot[i])){
			if(p[j1]&st[j0])continue;
			Inc(j2,1,cnts)if(!(st[j2]&cannot[i-2])&&!(p[j2]&cannot[i-3])&&!(p[j1]&cannot[i-1])){
				if((p[j2]&st[j1])||(p[j2]&p[j0]))continue;
				f[i][j0][j1]=max(f[i][j0][j1],f[i-1][j1][j2]+num[j0]);
			}
		}
	}

	Inc(i,2,n-1)Inc(j,1,cnts)Inc(k,1,cnts)ans=max(ans,f[i][j][k]);//在下懒得判了 
	cout<<ans<<"\n";
}
int main(){
	init();
	solv();
	return 0;
} 
/*
6 5
##.##
#...#
##..#
#....
....#
#.###
3

7 7
.......
.......
.......
.......
.......
.......
.......
6
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值