博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dtoj#4194. 「JOI 2019 Final」有趣的家庭菜园 3
阅读量:6872 次
发布时间:2019-06-26

本文共 3270 字,大约阅读时间需要 10 分钟。

题目描述:

家庭菜园专家 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
#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

你可能感兴趣的文章
Storm与Spark Streaming比较
查看>>
我的友情链接
查看>>
Exchange Server 运维管理01:Exchange中Active Directory 有什么用?
查看>>
linux系统管理之四:服务状态
查看>>
VMware View FAQ[一]
查看>>
【原创翻译】布尔值(boolean)
查看>>
三元运算式、lambda表达式、内置函数map、reduce、filter以及yield生成器
查看>>
MySQL分库分表分表后数据的查询(5th)
查看>>
iOS-点击图片放大,再次点击返回原视图 类似查看相册的功能
查看>>
JAVA -- stateless4j StateMachine 使用浅析(二)
查看>>
oracle checkpoint
查看>>
KVM虚拟化开源高可用方案(六)ISCSI ON DRBD搭建及常见故障处理
查看>>
android device related
查看>>
iOS 6 Beta3即将发布,iPhone面板谍照已经曝光
查看>>
hadoop 源码包编译
查看>>
微信小程序-多级联动
查看>>
Ubuntu配置MYSQL远程连接
查看>>
tcp端口扫描(python多线程)
查看>>
剑指offer-二叉树的镜像
查看>>
java实现二叉树
查看>>