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\n",res);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Jessie-/p/10443087.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值