萤火虫 解题报告
其中 \(N \leq 2e4\) 且 \(M \leq 1e4\)。
初看发现一行只有两个不同的字符构成,因此想到:
令 \(f_{i,c1,c2}\) 为使前 \(i\) 行符合条件,且第 \(i\) 行第一个字符为 \(c1\),第二个字符为 \(c2\) 的最小代价;
令 \(g_{i,c1,c2}\) 为使第 \(i\) 行第 \(i\) 行第一个字符为 \(c1\),第二个字符为 \(c2\) 的代价。
那么有转移方程:
\[f_{i,c1,c2}= \min{f_{i-1,c3,c4}+g_{i,c1,c2}}(c1 \ne c2,c3 \ne c4,c1 \ne c3,c2 \ne c4)
\]
通过分析得出这个算法的时间复杂度是 \(O(N*|S|^4 + N*|S|^2)\),这不能接受,于是有的人选择放弃这个方向......
但是我们发现,如果算法的复杂度可以降到 \((N*|S|^3)\),这便是可以接受的。
考虑优化,在递推中,只有枚举 \(f_{i-1,c3,c4}\) 的部分是有优化空间的。具体地,我们容易想到,给这东西排序,我们就可以从前往后枚举,符合条件的最小值。
但是这个枚举又有新的复杂度。我们尝试分析,我们会向后枚举,当且仅当 $c1=c3 $ 或 \(c2=c4\)(其他两个情况可以在排序前去掉),而这种状态只有 \(2|S|-1\) 个,因此这个算法的复杂度为 \(O(N*|S|^3+|S|^2log^2|S|)\),可以通过本题。
启示:细致的时间复杂度分析优于什么都不写空想。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define For(i,s,t) for(int i=s;i<=t;i++)
#define Down(i,s,t) for(int i=s;i>=t;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,pii> piii;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline int read(){
register int x=0;
char c=getchar();
while(c<'0' || '9'<c) c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
const int N=2e3+10,mod=1e4;
int n,m,f[N][27][27],g[N][27][27],a[N][N],cost[N][N][2],ans;
char s[N];
piii q[26*26*10];
int main()
{
freopen("firefly.in","r",stdin);
freopen("firefly.out","w",stdout);
n=read(),m=read();
For(i,1,n){
scanf("%s",s+1);
For(j,1,m) a[i][j]=s[j]-'a';
}
For(c,0,25)
For(i,1,n)
For(j,1,m)
if(a[i][j]!=c)
cost[c][i][j%2]++;
For(i,1,n)
For(c1,0,25)
For(c2,0,25)
if(c1!=c2)
g[i][c1][c2]=cost[c1][i][1]+cost[c2][i][0];
else
g[i][c1][c2]=inf;//防止出现同行相同
//上面这一部分时间复杂度为O(N*M*|S|)
For(c1,0,25)
For(c2,0,25)
f[1][c1][c2]=g[1][c1][c2];
For(i,2,n){
For(c1,0,25)
For(c2,0,25)
q[c1*26+c2]=make_pair(f[i-1][c1][c2],make_pair(c1,c2));
sort(q,q+676,less<piii>());
For(c1,0,25)
For(c2,0,25)
if(c1!=c2){
For(j,0,675)
if(c1!=c2 && c1!=q[j].second.first && c2!=q[j].second.second){
f[i][c1][c2]=q[j].first+g[i][c1][c2];
break;
}
}
else
f[i][c1][c2]=inf;
}
ans=inf;
For(c1,0,25)
For(c2,0,25)
ans=min(ans,f[n][c1][c2]);
printf("%d",ans);
return 0;
}