花园游戏 【动态规划】
(原题)题目链接:看樱花 https://vijos.org/p/1627
描述
“妹妹背着洋娃娃,走到花园看樱花” – 我整个人都Hello kitty了。
好了,闲话就说到这里,已知:这是一个1×N的花园(虽然比较奇怪),被分成了N个格子,每个格子里有一种神奇的樱花(我也不知道为什么神奇,反正洋娃娃看着高兴),看到第i个格子上的花洋娃娃会得到不同的满足度Ci(每个花的满足度只被计算一次)。现在妹妹会背着洋娃娃从任意格子走进花园,当然从第i个格子进去会消耗Di个单位的满足度,然后游历花园,在一个格子向右走需要耗费R个单位的满足度,向左走需要耗费L个单位的满足度,最后从第i个格子出花园又要耗费Fi个单位的满足度。
接下来,我们需要设计一套游历方案,使得最终获得的总满足度最高(太低的话洋娃娃会……)
格式
输入格式
第一行依次给出三个正整数N,L,R。
第二行有N个整数,第i个数为Di。
第三行有N个整数,第i个数为Fi。
第四行有N个整数,第i个数为Ci。
输出格式
仅需要输出一行包括一个整数,表示最大获得的满足度为多少。
样例1
样例输入1
5 1 1
1 1 1 1 1
1 1 1 1 1
1 1 3 1 1
样例输出1
1
限制
各个测试点1s
提示
对于30%数据,N<=10。
对于60%数据,N<=100。
对于100%数据,N<=1000。
来源
Mrain 原创
NOIP 2009·Dream Team 模拟赛 第一期 第三题
分析:
本来我是用的贪心的,但很无奈,错误的贪心;
错误的贪心:
直接向左刷一遍单向最优解Dp[i],再从左往右不计满足度去寻找最优解
初始化Dp[i]
同理向右刷一遍单向最优解Dp[i],再从右往左不计满足度去寻找最优解
输出两个最优解的较大值
错误就在于忽略了往返后能继续增长的满足度(即走回来继续走的所新得到的满足度都被我吃了)
改进所得的动态规划:
既然我错在往返后能继续增长的满足度,那我直接枚举往返后的两个端点就好了呀
打个栗子:
我们计算 i -> j 的最优情况 (i > j 即 i 在 j 右边)
也就是计算 i -> j 的路上的满足度(和损耗) 以及 i 向右再回来能得到的最大值(也就是我之前贪心的东西)
见下面部分代码
1 /* 2 Right[j] :走到 j 的左面再回来所能增加的满足度(为零代表不走) 3 Left[i] :同理 4 Pre :用前缀和的方式记录 1 -> i 或者 1 -> j 的满足度 5 */ 6 7 int ans=-1e9; 8 for(int i=1;i<=N;i++) 9 for(int j=i;j<=N;j++){ // 从 i 进去 从 j 出来 10 int t=Right[j]+Left[i]+Pre[j]-Pre[i-1]-R*(j-i)-D[i]-F[j]; 11 ans=max(t,ans); 12 }
i 在 j 的左边的情况同理哦
Code
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 int N; 8 int L,R; 9 int D[1005]; 10 int F[1005]; 11 int C[1005]; 12 void Init(){ 13 scanf("%d%d%d",&N,&L,&R); 14 for(int i=1;i<=N;i++) 15 scanf("%d",&D[i]); 16 for(int i=1;i<=N;i++) 17 scanf("%d",&F[i]); 18 for(int i=1;i<=N;i++) 19 scanf("%d",&C[i]); 20 return ; 21 } 22 int Pre[1005]; 23 int Left[1005]; 24 int Right[1005]; 25 26 void Solve(){ 27 Right[N]=0,Left[1]=0; 28 for(int i=2;i<=N;i++) 29 Left[i]=max(Left[i-1]-L-R+C[i-1],Left[i]); 30 for(int i=N-1;i>0;i--) 31 Right[i]=max(Right[i],Right[i+1]-L-R+C[i+1]); 32 33 Pre[1]=C[1];Pre[0]=0; 34 for(int i=2;i<=N;i++) 35 Pre[i]=Pre[i-1]+C[i]; 36 int ans=-1e9; 37 for(int i=1;i<=N;i++) 38 for(int j=i;j<=N;j++){ 39 int t=Right[j]+Left[i]+Pre[j]-Pre[i-1]-R*(j-i)-D[i]-F[j]; 40 ans=max(t,ans); 41 } 42 for(int i=1;i<=N;i++) 43 for(int j=1;j<=i;j++){ 44 int t=Left[j]+Right[i]+Pre[i]-Pre[j-1]-L*(i-j)-D[i]-F[j]; 45 ans=max(t,ans); 46 } 47 printf("%d\n",ans); 48 return ; 49 } 50 int main(){ 51 Init(); 52 Solve(); 53 return 0; 54 }