• dtoj#4194. 「JOI 2019 Final」有趣的家庭菜园 3


    题目描述:

    家庭菜园专家 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 le N le 400$。

    算法标签:dp

    思路:

    令 $f[i][j][k][3]$ 表示前 $i$ 个数,有 $j$ 个红色,有 $k$ 个绿色,最后一个 $0/1/2$ 分别表示最后一个也就是第 $i$ 个 植物是哪种颜色,同时也可以推出有几株黄色植物。

    转移考虑利用初始局面和当前的 $i,j,k$ 来表示每次把哪株植物移到当前位置需要用的代价。

    因为要最小化代价,所以同种颜色之间相对位置不变,所以我们如果算出当前要放某个颜色的植物需要多少代价是可以 $O(1)$ 得到的。

    具体式子如下(比如我们当前位置要填红色植物):

    $f[i][j][k][0]=f[i][j-1][k]+cost(i-j-k,j-1,k,0)$

    以下代码:

    #include<bits/stdc++.h>
    #define il inline
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=403,inf=1e8;
    char s[N];
    int n,sum[N][3],g[N][3],nt[3],a[N],f[2][N][N][3],op;
    il int read(){
        int x,f=1;char ch;
        _(!)ch=='-'?f=-1:f;x=ch^48;
        _()x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    il int cost(int x,int y,int z,int num){
        int now=x+y+z+1;
        if(x>nt[0])return inf;if(y>nt[1])return inf;
        if(z>nt[2])return inf;
        if(num==0){
            if(x+1>nt[0])return inf;
            int id=sum[x+1][0];
            int res=sum[x+1][0]-now;
            res+=max(0,y-g[id][1])+max(0,z-g[id][2]);
            return res;
        }
        if(num==1){
            if(y+1>nt[1])return inf;
            int id=sum[y+1][1];
            int res=sum[y+1][1]-now;
            res+=max(0,x-g[id][0])+max(0,z-g[id][2]);
            return res;
        }
        if(num==2){
            if(z+1>nt[2])return inf;
            int id=sum[z+1][2];
            int res=sum[z+1][2]-now;
            res+=max(0,x-g[id][0])+max(0,y-g[id][1]);
            return res;
        }
    }
    int main()
    {
        n=read();scanf(" %s",s+1);
        for(int i=1;i<=n;i++){
            if(s[i]=='R')a[i]=1;
            else if(s[i]=='Y')a[i]=2;
        }
        for(int i=1;i<=n;i++)sum[++nt[a[i]]][a[i]]=i;
        for(int i=1;i<=n;i++){
            for(int j=0;j<4;j++){
                g[i][j]=g[i-1][j];
            }
            g[i][a[i]]++;
        }
        for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)for(int z=0;z<4;z++)f[0][j][k][z]=inf;
        f[0][0][0][0]=sum[1][0]-1;f[0][1][0][1]=sum[1][1]-1;f[0][0][1][2]=sum[1][2]-1;
        for(int i=2;i<=n;i++){
            op^=1;
            for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)for(int z=0;z<4;z++)f[op][j][k][z]=inf;
            for(int j=0;j<=i&&j<=(i+1<<1);j++){
                for(int k=0;k<=i-j&&k<=(i+1<<1);k++){
                    if(i-j-k)f[op][j][k][0]=min(f[op^1][j][k][1]+cost(i-j-k-1,j,k,0),f[op][j][k][0]);
                    if(i-j-k)f[op][j][k][0]=min(f[op^1][j][k][2]+cost(i-j-k-1,j,k,0),f[op][j][k][0]);
                    if(j)f[op][j][k][1]=min(f[op^1][j-1][k][0]+cost(i-j-k,j-1,k,1),f[op][j][k][1]);
                    if(j)f[op][j][k][1]=min(f[op^1][j-1][k][2]+cost(i-j-k,j-1,k,1),f[op][j][k][1]);
                    if(k)f[op][j][k][2]=min(f[op^1][j][k-1][0]+cost(i-j-k,j,k-1,2),f[op][j][k][2]);
                    if(k)f[op][j][k][2]=min(f[op^1][j][k-1][1]+cost(i-j-k,j,k-1,2),f[op][j][k][2]);
                }
            }
        }
        int res=inf;
        for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)for(int z=0;z<4;z++)res=min(res,f[op][j][k][z]);
        if(res<0)res=0;
        if(res==inf)puts("-1");else printf("%d
    ",res);
        return 0;
    }
    View Code
  • 相关阅读:
    python基础——列表生成式
    python基础——迭代
    python基础——切片
    python基础——递归函数
    python基础——函数的参数
    python基础——使用dict和set
    python基础——使用list和tuple
    python基础——字符串和编码
    堆——神奇的优先队列(下)
    堆——神奇的优先队列(上)
  • 原文地址:https://www.cnblogs.com/Jessie-/p/10443087.html
Copyright © 2020-2023  润新知