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; }