题目大意: 在一个网格图上放置 3 3 3 个L,要求两两L不重叠且不放在装饰品上。
题解
比较明显的插头dp,注意到轮廓线上最多只有 3 3 3 个插头,如果用二进制记录需要 2 30 2^{30} 230 的大小,发现其中其实有很多无用状态,可以用哈希压一下,不过实际上直接记录三个插头的位置就好, 3 0 3 = 27000 30^3=27000 303=27000,完全可以接受,然后还需要记录一下现在放了几个L。
关于转移,分四种情况讨论:
一开始想得很简单,然后越写越多问题,代码改了很多地方,上面的思路也是改过之后的成果……建议自己思考然后独立写一遍,确实可以提高思维能力。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
int n,m;
char s[40];
struct Pos{
Pos(){
}
int a[3];
void sort(){
if(a[0]>a[1])swap(a[0],a[1]);
if(a[1]>a[2])swap(a[1],a[2]);
if(a[0]>a[1])swap(a[0],a[1]);
}
Pos add(int x){
Pos re=*this;
if(!a[2])re.a[2]=x;
else if(!a[1])re.a[1]=x;
else re.a[0]=x;
re.sort();
return re;
}
bool operator !=(Pos B)const{
return a[0]!=B.a[0]||a[1]!=B.a[1]||a[2]!=B.a[2];
}
};
struct state{
Pos S;int tot;ll z;}sta[2][200010];
int id[32][32][32][4],t[2],cur=0;
void add(Pos x,int tot,ll z){
int &ID=id[x.a[0]][x.a[1]][x.a[2]][tot];
if(sta[cur][ID].S!=x||sta[cur][ID].tot!=tot)
ID=++t[cur],sta[cur][ID]=(state){
x,tot,0};
sta[cur][ID].z+=z;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<2;i++)
for(int j=0;j<=200000;j++)
sta[i][j].S.a[0]=-1;
sta[cur][++t[0]].S.a[0]=0;
sta[cur][t[0]].z=1;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
cur^=1;
for(int k=1;k<=t[cur];k++)
sta[cur][k].S.a[0]=-1;
t[cur]=0;
for(int k=1;k<=t[cur^1];k++){
int x=0,y=0,tot=sta[cur^1][k].tot;
Pos z=sta[cur^1][k].S;
if(tot==-1)continue;
for(int p=0;p<3;p++){
if(sta[cur^1][k].S.a[p]==j)x=1,z.a[p]=0;
if(sta[cur^1][k].S.a[p]==j+1)y=1,z.a[p]=0;
}
if(s[j]=='#'){
//有装饰品时,不能有插头,否则就不合法
if(!(x|y))add(z,tot,sta[cur^1][k].z);
continue;
}
z.sort();
if(x&&y)continue;//上左都有插头,不合法
if(y){
add(z.add(j),tot,sta[cur^1][k].z);
add(z.add(j+1),tot,sta[cur^1][k].z);
}else if(x){
add(z,tot,sta[cur^1][k].z);
add(z.add(j+1),tot,sta[cur^1][k].z);
}else{
if(tot<3)add(z.add(j),tot+1,sta[cur^1][k].z);
add(z,tot,sta[cur^1][k].z);
}
}
}
for(int j=1;j<=t[cur];j++){
Pos z=sta[cur][j].S;
for(int k=0;k<3;k++)
if(sta[cur][j].S.a[k]){
sta[cur][j].S.a[k]++;
if(sta[cur][j].S.a[k]>m+1)sta[cur][j].tot=-1;
}
sta[cur][j].S.sort();
z=sta[cur][j].S;
}
}
ll ans=0;
for(int j=1;j<=t[cur];j++)
if(!sta[cur][j].S.a[2]&&sta[cur][j].tot==3)ans+=sta[cur][j].z;
printf("%lld",ans);
}