问题描述

家庭菜园专家 JOI 先生在他的家庭菜园中种植了一种叫 Joy 草的植物。在他的菜园里,有 N 个花盆自东向西摆放,编号分别为 \(1, \ldots, N\)。每个花盆中有一株 Joy 草。

春天到了,JOI 先生注意到 Joy 草如他期望地长出了各种颜色的叶子,但他也发现 Joy 草的生长速度没有他期望的那么快。他查阅了书籍,找到了草的以下特点:

  • Joy 草有三种品种,分别会长出红色、绿色和黄色的叶子。

  • 如果两株同一颜色的 Joy 草紧密相邻,它们的生长速度就会减慢。

因此,JOI 先生决定重新摆放花盆,使得没有两株相邻的 Joy 草颜色相同。

花盆非常沉重,因此 JOI 先生每次只能交换相邻的两个花盆。形式化的说,JOI 先生每次操作可以选择一个$ i (1 \le i < N)$,然后交换花盆 i 和花盆 i+1。

请编写一个程序,计算最少的交换次数。

解析

考虑将当前在哪个位置作为阶段,那么我们需要知道前一个阶段的状态。显然,我们需要知道交换后1到i-1每种颜色的草的数量,转移时也需要知道第i-1为放的是什么草。设\(f[i][j][k][0/1/2]\)表示当前完成前i个花盆,交换后有j个红花,k个黄花,i-j-k个绿花,第i为放第0/1/2种花时的最小交换次数。不妨设将第x朵颜色为op的花移动到y位置的交换次数为\(cost(op,x,y)\),那么,我们有如下状态转移方程:
\[f[i][j][k][0]=min(f[i-1][j][k][1]+cost(0,i-j-k+1,i),f[i-1][j][k][2]+cost(0,i-j-k+1,i))\]
其他的同理。现在讨论如何\(O(1)\)\(cost\)值。记第x朵颜色为op的花在原序列中出现的位置为\(pos[op][x]\),那么将这朵花移动到i位置需要\(pos[op][x]-i\)步。但是,有可能在之前移动了其他颜色的花到区间\([1,i]\)中,所以移动的长度也要大于之前的值。具体见代码。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 402
using namespace std;
const int inf=1<<30;
int n,i,j,k,x,f[2][N][N][3],pos[3][N],sum[3][N],a[N],cnt[3];
char c[N];
int cal(int op,int g,int r,int y)
{
    int m=g+r+y+1;
    if(g>cnt[0]||r>cnt[1]||y>cnt[2]) return inf;
    if(op==0){
        if(g+1>cnt[0]) return inf;
        int p=pos[0][g+1];
        return p-m+max(0,r-sum[1][p])+max(0,y-sum[2][p]);
        //sum[op][p]表示在[1,p]中有多少颜色为op的花。
        //r-sum[1][p]即在此之前移动了多少红花到[1,i]中来,那么绿花移动的长度也要加上r-sum[1][p]。
        //当然,如果没有多移动,就不需要加了。
        //y-sum[2][p]也是同样的道理。
    }
    else if(op==1){
        if(r+1>cnt[1]) return inf;
        int p=pos[1][r+1];
        return p-m+max(0,g-sum[0][p])+max(0,y-sum[2][p]);
    }
    else{
        if(y+1>cnt[2]) return inf;
        int p=pos[2][y+1];
        return p-m+max(0,g-sum[0][p])+max(0,r-sum[1][p]);
    }
}
int main()
{
    cin>>n>>c;
    for(i=1;i<=n;   freopen("home.in","r",stdin);
    freopen("home.out","w",stdout);i++){
        if(c[i-1]=='R') a[i]=1;
        else if(c[i-1]=='Y') a[i]=2;
    }
    for(i=1;i<=n;i++) pos[a[i]][++cnt[a[i]]]=i;
    for(i=1;i<=n;i++){
        for(j=0;j<3;j++) sum[j][i]=sum[j][i-1];
        sum[a[i]][i]++;
    }
    memset(f,0x3f,sizeof(f));
    f[0][0][0][0]=pos[0][1]-1;
    f[0][1][0][1]=pos[1][1]-1;
    f[0][0][1][2]=pos[2][1]-1;
    for(i=2;i<=n;i++){
        x^=1;
        memset(f[x],0x3f,sizeof(f[x]));
        for(j=0;j<=(i+1)/2;j++){
            for(k=0;k<=i-j&&k<=(i+1)/2;k++){
                if(i-j-k){
                    f[x][j][k][0]=min(f[x][j][k][0],f[x^1][j][k][1]+cal(0,i-j-k-1,j,k));
                    f[x][j][k][0]=min(f[x][j][k][0],f[x^1][j][k][2]+cal(0,i-j-k-1,j,k));
                }
                if(j){
                    f[x][j][k][1]=min(f[x][j][k][1],f[x^1][j-1][k][0]+cal(1,i-j-k,j-1,k));
                    f[x][j][k][1]=min(f[x][j][k][1],f[x^1][j-1][k][2]+cal(1,i-j-k,j-1,k));
                }
                if(k){
                    f[x][j][k][2]=min(f[x][j][k][2],f[x^1][j][k-1][0]+cal(2,i-j-k,j,k-1));
                    f[x][j][k][2]=min(f[x][j][k][2],f[x^1][j][k-1][1]+cal(2,i-j-k,j,k-1));
                }
            }
        }
    }
    int ans=inf;
    for(i=0;i<=n;i++){
        for(j=0;j<=n;j++){
            for(k=0;k<3;k++) ans=min(ans,f[x][i][j][k]);
        }
    }
    if(ans>=1000000000) cout<<"-1"<<endl;
    else if(ans<0) cout<<"0"<<endl;
    else cout<<ans<<endl;
    return 0;
}
01-01 21:23