2021 ICPC SH C. Strange Matrices
考虑状压DP,原本想的是一行行转移,而最后一行的状态需要记\(3^m\)个,然后直接转移的话,需要枚举该行的2变成0或1,以及0是否选入集合,又是一个\(3^m\),复杂度爆炸,且难以优化状态。(原本还莽了一下,以为可以优化掉一些地方,然而全是2的数据就凉了。)
那么只能优化转移的方式了。所以这个时候应该改变一下传统的思路:并不是非得一次转移一行(这样枚举这行的情况是很浪费的)。对于一行,也可以按照行间DP那样的思路,直接从左往右一个个考虑,然后记录状态。问题在于要记录哪些状态,发现对于该行之前考虑过的位置,上一行的状态已经没用了,即只要记录下面轮廓线的状态即可。
对于同行之间的影响,我的做法是记录目前的0所在的那段是【没有关键点/按照有关键点考虑但目前还没有关键点/已有关键点】这三种状态,遇到一个1直接把第二种状态舍掉(非法)即可,效率\(O(3nm*3^m)\) ,(\(n,m\)其实可以更大一点)。
出错点:转移的时候,被转移的量一定要取min!!!
代码
#include<bits/stdc++.h>
using namespace std;
const int N=10,M=10005;
int n,m,a[N][N],f[N][N][M][3],pw3[N];
int main()
{
//freopen("1.in","r",stdin);
//int t=11;
//int s=3*t*t; for(int i=1;i<=t;i++) s*=3; cout<<s<<endl;
cin>>n>>m;
pw3[0]=1;
for(int i=1;i<=m;i++) pw3[i]=pw3[i-1]*3;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char ch;
scanf(" %c",&ch);
a[i][j]=ch-'0';
}
}
memset(f,0x3f,sizeof(f));
f[1][0][0][0]=f[1][0][0][1]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int s=0;s<pw3[m];s++){
int k=(s/pw3[j-1])%3,u=pw3[j-1],t=s-u*k;
//(i,j)=0
if(a[i][j]!=1){
//not chose (i,j)
int v=s;
if(!k) v+=u;
for(int p=0;p<3;p++){
if(!p) f[i][j][v][p]=min(f[i][j][v][p],f[i][j-1][s][p]);
else f[i][j][s][p]=min(f[i][j][s][p],f[i][j-1][s][p]);
}
//chose(i,j)
v=t+u+u;
f[i][j][v][2]=min(f[i][j][v][2],min(f[i][j-1][s][1],f[i][j-1][s][2])+1);
}
//(i,j)=1
if(a[i][j] && k!=1){
int v=min(f[i][j-1][s][0],f[i][j-1][s][2]);
for(int p=0;p<2;p++) f[i][j][t][p]=min(f[i][j][t][p],v);
//!!! must use the min()!!!
}
}
}
for(int s=0;s<pw3[m];s++){
int v=min(f[i][m][s][0],f[i][m][s][2]);
f[i+1][0][s][0]=f[i+1][0][s][1]=v;
}
}
int ans=M;
for(int s=0;s<pw3[m];s++){
int pd=1;
for(int j=1;j<=m;j++) if((s/pw3[j-1])%3==1) pd=0;
if(pd) ans=min(ans,f[n+1][0][s][0]);
}
cout<<ans<<endl;
return 0;
}
浙公网安备 33010602011771号