P7171 [COCI2020-2021#3] Selotejp
P7171 题解
2021.10.29 修改了一些语言表述问题。
题意:
有
分析:
一道经典的 dp 问题,考虑普通状压转移过于复杂,还会超时,所以使用轮廓线 dp。
我们设
(此时二进制状态从低位到高位应分别对应前 m 的数、前 m-1 的数等等,以此类推)
(对于“前 i 的数”的顺序的定义,可以理解为矩阵一般的搜索顺序)
解决:
确定状态后,我们考虑该如何转移。
对于当前所找的元素,如果为"#",则
以此类推,
如果当前元素为".",则直接转移贡献即可。
(可能有点难读懂,那就展现读者强大的阅读能力吧!)
代码:
#include<bits/stdc++.h>
using namespace std;
int dp[1005][15][2050];
int n,m,M;
char c[1005][15];
int ans;
int maxn=1000000007;
int main()
{
cin>>n>>m;M=1<<m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>c[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<M;k++)
dp[i][j][k]=maxn;
if(c[1][1]=='#') dp[1][1][0]=dp[1][1][1<<m-1]=1;
else dp[1][1][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<M;k++)
{
if(dp[i][j][k]>=maxn) continue;
int nj=j%m+1,ni=i;
if(j==m) ni=i+1;
if(c[ni][nj]=='#')
{
if(nj>1&&!(k&1<<m-1)&&c[i][j]=='#') dp[ni][nj][k>>1]=min(dp[ni][nj][k>>1],dp[i][j][k]);
else dp[ni][nj][k>>1]=min(dp[ni][nj][k>>1],dp[i][j][k]+1);
if(ni>1&&(k&1)&&c[ni-1][nj]=='#') dp[ni][nj][(k>>1)+(1<<m-1)]=min(dp[ni][nj][(k>>1)+(1<<m-1)],dp[i][j][k]);
else dp[ni][nj][(k>>1)+(1<<m-1)]=min(dp[ni][nj][(k>>1)+(1<<m-1)],dp[i][j][k]+1);
}
else dp[ni][nj][k>>1]=min(dp[ni][nj][k>>1],dp[i][j][k]);
}
ans=maxn;
for(int i=0;i<M;i++)
ans=min(ans,dp[n][m][i]);
cout<<ans;
return 0;
}
朴素~~奇丑~~的代码。