萤火虫 解题报告

题目描述
其中 \(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;
}
posted @ 2025-07-09 21:46  XiaoZi_qwq  阅读(20)  评论(1)    收藏  举报