题目
10组数据。
每组数据:一个
n
∗
m
n*m
n∗m的方阵,每个点上要么有敌人,要么有炮台。
每个炮台只能够选择上,左,右三个方向中的一个。该方向上所有的敌人都被消灭。
问是否存在一种方案,使得所有的敌人被消灭。
数据范围:
T
<
=
10
,
n
,
m
<
=
50
,
L
<
=
16
T<=10,n,m<=50,L<=16
T<=10,n,m<=50,L<=16
L表示炮台的个数。
题解
三个套路:
降底数,和普通的贪心。
bitset优化
考虑状压,
3
16
3^{16}
316很大,再乘个10跑不过。
考虑将3变成2.
如果每个炮台只能选择左,右两个方向,那么怎么消灭所有的敌人?
显然,如果一行里有两个炮台,那么这行的敌人全能被消灭。
如果只有1个炮台,且它在这行的最边上,那么这行的敌人也全能被消灭。
因此,枚举哪个炮台选择上的方向,哪些不选择即可。
bitset维护敌人位置,左右方向的炮台的位置。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<bitset>
#define N 52
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,s,gs,sht,res;
int X[N],Y[N];
int T,cs;
bitset<N>b[N],bt[N],b1[N],b2[N];
bitset<N>L[N],R[N],c1,c2;
int _2[N];
char ch;
bool ia;
int main(){
scanf("%d",&T);
_2[1]=1;
fo(i,2,20)_2[i]=_2[i-1]<<1;
fo(cs,1,T){
scanf("%d%d",&n,&m);
fo(i,1,n){
b[i].reset(),b1[i].reset();
L[i].reset(),R[i].reset();
}
sht=0;ch=0;
ia=0;gs=0;
fo(i,1,n)fo(j,1,m){
ch=getchar();
while((ch^'.')&&(ch^'E')&&(ch^'L'))ch=getchar();
if(ch=='E'){
b[i][j]=1;
sht++;
}
if(ch=='L'){
gs++;
X[gs]=i;
Y[gs]=j;
b1[i][j]=1;
}
}
fo(i,1,n){
fo(j,1,i)L[i][j]=1;
fo(j,i,n)R[i][j]=1;
}
fo(s,0,_2[gs+1]-1){
ia=1;
res=sht;
fo(i,1,n)bt[i]=b[i],b2[i]=b1[i];
fo(i,1,gs)if(s&_2[i]){
b2[X[i]][Y[i]]=0;
fo(j,1,X[i]-1){
if(bt[j][Y[i]]==1){
bt[j][Y[i]]=0;
}
}
}
fo(i,1,gs)if(!(s&_2[i])){
if(b2[X[i]].count()>=2){
bt[X[i]].reset();
continue;
}
c1=bt[X[i]]&L[Y[i]];
c2=bt[X[i]]&R[Y[i]];
if(c1.count()&&c2.count()){
ia=0;
break;
}else bt[X[i]].reset();
}
fo(i,1,n){
if(bt[i].count()){
ia=0;
break;
}
}
if(ia)break;
}
if(ia)printf("Possible\n");else printf("Impossible\n");
}
return 0;
}