博主学习本题的经过嘤嘤嘤:

7.22 : 听学长讲(一知半解)——自己推(推不出来)——网上看题解——以为自己会了(网上题解是错的)——发现错误以后又自己推(没推出来)——给学长发邮件——得到正确解法——按着学长思路又推一遍——最后理解

(前后的“学长”不是同一个人)

7.23 : 写出代码,完善细节。

(建议改成:西   天   取   经)

首先,网上对于这道题的题解绝大部分是错误的!(比如洛谷上的部分题解)

用LIS做是不行的

玄学贪心是不行的

dp转移方程不能自圆其说是不行的

即使是AC代码也不一定是正确的(2009年的省选,数据太太太太太太太太太太太太太太水了嘤嘤嘤)

废话说完了

~~~~~~~~~~~~~~~嘤嘤嘤来自蒟蒻OIerOrzer的分割线啦嘤嘤嘤~~~~~~~~~~~~~~~~

以下为正文部分嘤嘤嘤:

考虑把一个数列分成两个集合,有a[i]的为一个集合,没有a[i]的为一个集合~

我们定义状态转移方程dp[i][j]表示对于前i个数,有a[i]的集合的长度为j,没有a[i]的集合的最后一个数的最小值为dp[i][j](神仙定义)

也就是说,现在有两个集合,其中一个有a[i],另一个没有a[i]。尝试把a[i+1]放到其中一个集合中。

1.尝试把a[i+1]放到有a[i]的集合当中。那么需要满足的先决条件就是:a[i+1]>a[i].

此时更新dp[i+1][j+1](因为把a[i]放到长度为j的集合中,于是长度++)此时没有a[i+1]的集合同时也是没有a[i]的集合,换句话来说,这个转移对没有a[i]的集合是没有改变的,所以,dp[i+1][j+1]可以直接由dp[i][j]继承过来。

2.尝试把a[i+1]放到没有a[i]的集合当中。那么需要满足的先决条件就是:a[i+1]>dp[i][j].

此时更新dp[i+1][i-j+1](原来没有a[i]的集合的长度为(i-j),把a[i+1]放进去,长度++)既然把a[i+1]放到了没有a[i]的集合中,那么,没有a[i+1]的集合的最后一个数就是a[i],于是,用a[i]去更新dp[i+1][i-j+1];

(真绕啊嘤嘤嘤)

上代码嘤嘤嘤:

 1 #include <bits/stdc++.h>
2 using namespace std;
3 const int maxn=2000+10;
4 int dp[maxn][maxn],a[maxn];
5 int n,m;
6 void Solve(){
7 scanf("%d",&m);
8 while(m--){
9 scanf("%d",&n);
10 memset(dp,0x3f,sizeof(dp));
11 memset(a,0x3f,sizeof(a));
12 for(int i=1;i<=n;++i) scanf("%d",&a[i]);
13 dp[1][1]=-1;//因为数据中可能有0,因此不能初始化为0;
14 for(int i=1;i<=n;++i){
15 for(int j=1;j<=i;++j){
16 if(a[i+1]>a[i]) dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]);
17 if(a[i+1]>dp[i][j]) dp[i+1][i-j+1]=min(dp[i+1][i-j+1],a[i]);
18 }
19 }
20 if(dp[n][n/2]>1e8) printf("No!\n");
21 //没有更新,说明不能将原序列合法地平分成两部分,就输出No;
22 else printf("Yes!\n");
23 }
24 }
25 int main(){
26 Solve();
27 return 0;
28 }

完结撒花嘤嘤嘤~(然而并没有)

上辈子的题了(大雾)(所以这就是火星水吗)

但是在解决上一道题后会发现对于这道题会有不一样的理解~~~

我们先定义dp[i][j]表示小烈1走到i,小烈2走到j时的最大收益。且默认小烈1始终在小烈2前面,且前j个已经被送完。

换句话来说,其实根本没有小烈1和小烈2(只是我们yy出来的)或者说,并不是划分了“小烈1”和“小烈2”这两个抽象的概念,划分的应该是“送了a[i]的小烈”和“没有送a[i]的小烈”!

(woc这不就是上一道题吗,太像了好叭)

现在我们换一种表达方式来定义dp[i][j]:

dp[i][j]表示前i个客人,没有送过a[i]的小烈最后一个送的是a[j]时的最大收益。

默认j小于i,且前j个一定已经送过了(如果前j个有没有送过的,那就不合法了,因为两个小烈不能回头)

现在考虑a[i+1]由谁送。

1.由送了a[i]的小烈送。所以,没有送a[i]的小烈同样没有送a[i+1]。也就是说,这个转移完成后,没有送a[i]的小烈原来在j,现在还是在j,没有变化。

所以用(dp[i][j]+a[i]*a[i+1])更新(dp[i+1][j])。

2.由没有送a[i]的小烈送。所以,送了a[i]的小烈没有送a[i+1]。也就是说,没有送a[i+1]的小烈最后一个送的是a[i]!

所以用(dp[i][j]+a[j]*a[i+1])更新(dp[i+1][i]).

(和刚才那个一样绕嘤嘤嘤)

上代码:

 1 #include <bits/stdc++.h>
2 using namespace std;
3 const int maxn=2500+10;
4 int dp[maxn][maxn],a[maxn],ans;
5 void Solve(){
6 int n;scanf("%d",&n);
7 for(int i=1;i<=n;++i) scanf("%d",&a[i]);
8 for(int i=1;i<=n;++i){
9 for(int j=0;j<i;++j){//j的范围要搞清楚哦;
10 dp[i+1][j]=max(dp[i+1][j],dp[i][j]+a[i]*a[i+1]);
11 dp[i+1][i]=max(dp[i+1][i],dp[i][j]+a[j]*a[i+1]);
12 }
13 }
14 for(int i=0;i<n;++i) ans=max(ans,dp[n][i]+a[i]*a[n]);
15 /*现实中的小烈是从1走到n,又从n回去,在dp方程里面我们把一个小烈拆成了两个,
16 其中一个表示现实中正向走的部分,另一个表示现实中反向走的部分(但是令这一个
17 反向走的小烈反过来走,就是正着走)也就是说,当现实中小烈走到n,开始返回的
18 时候,a[n]与dp方程中定义的小烈2经过的最后一个a[i](也就是反着走的第一个
19 a[i])是要产生一个值的,而这个值要加到答案里面才能得到最终结果*/
20 printf("%d",ans);
21 }
22 int main(){
23 Solve();
24 return 0;
25 }

嘤嘤嘤

完结撒花花!!!

[HNOI2009]双递增序列(洛谷P4728)+小烈送菜(内部训练题)——奇妙的dp的更多相关文章

  1. 【BZOJ1489】[HNOI2009]双递增序列(动态规划)

    [BZOJ1489][HNOI2009]双递增序列(动态规划) 题面 BZOJ 洛谷 题解 这\(dp\)奇奇怪怪的,设\(f[i][j]\)表示前\(i\)个数中,第一个数列选了\(j\)个数,第二 ...

  2. 小烈送菜——奇怪的dp

    小烈送菜 题目描述 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴 ...

  3. 线性DP之小烈送菜

    小烈送菜 小烈一下碰碰车就被乐满地的工作人员抓住了.作为扰乱秩序的惩罚,小烈必须去乐满地里的"漓江村"饭店端盘子. 服务员的工作很繁忙.他们要上菜,同时要使顾客们尽量高兴.一位服务 ...

  4. 方格取数(简单版)+小烈送菜(不知道哪来的题)-----------奇怪的dp增加了!

    一.方格取数: 设有N*N的方格图(N<=20),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0. 某人从图的左上角的A(1,1) 点出发,可以向下行走,也可以向右走,直到到达右下 ...

  5. 【题解】洛谷P1373 小a和uim之大逃离(坐标DP)

    次元传送门:洛谷P1373 思路 设f[i][j][t][1/0]表示走到(i,j)时 小a减去uim的差值为t 当前是小a取(0) uim取(1) 那么转移就很明显了 f[i][j][t][]=(f ...

  6. P4728 [HNOI2009]双递增序列

    题意 这个DP状态有点神. 首先考虑一个最暴力的状态:\(f_{i,j,k,u}\)表示第一个选了\(i\)个,第二个选了\(j\)个,第一个结尾为\(k\),第二个结尾为\(u\)是否可行. 现在考 ...

  7. [luogu4728 HNOI2009] 双递增序列 (dp)

    传送门 Solution 前几天刚做了类似题,这种将一个序列拆分为两个单调序列的题一般都是设\(dp[i]\)表示i为一个单调序列的末尾时,另一个序列的末尾是多少 然后应用贪心的思想,在这道题中就是让 ...

  8. [HNOI2009]双递增序列(动态规划,序列dp)

    感觉这个题还蛮难想的. 首先状态特别难想.设\(dp[i][j]\)表示前i个数,2序列的长度为j的情况下,2序列的最后一个数的最小值. 其中1序列为上一个数所在的序列,2序列为另外一个序列. 这样设 ...

  9. [HNOI2009]双递增序列

    不难发现本题贪心是不好做的,可以考虑 \(dp\). 首先的一个想法就是令 \(dp_{i, j, k, l}\) 表示当前选到第 \(i\) 个位置,当前第一个序列选了 \(j\) 个数,当前第一个 ...

随机推荐

  1. Mac上Markdown的使用

    Markdown是什么,且听我快快道来. 20年前,我第一次接触互联网,当时还是用 28.8k的猫拨号. 我从一本<电脑报>附赠的光盘里,找到了 台湾版的"烘培机"(烘 ...

  2. 358 day09字节流、字符流

    day09[字节流.字符流] 主要内容 IO流 字节流 字符流 异常处理 Properties 教学目标 [ ] 能够说出IO流的分类和功能 [ ] 能够使用字节输出流写出数据到文件 [ ] 能够使用 ...

  3. 在树莓派用C#+Winform实现传感器监测

    最近学校里发了个任务,说要做一个科技节小发明,然后我就掏出我的树莓派准备大干一场. 调料 Raspberry Pi 3B+ 树莓派GPIO扩展板 3.5寸电容触摸屏(GPIO接口) 土壤湿度传感器(G ...

  4. Django学习day07随堂笔记

    今日考题 """ 今日考题 1.必知必会N条都有哪些,每个都是干啥使的 2.简述神奇的双下划线查询都有哪些方法,作用是什么 3.针对多对多外键字段的增删改查方法有哪些,各 ...

  5. ecshop后台设置模板的地方显示自己新建模板的操作界面

    我建立了一个叫test.dwt文件怎样在后台设置模板里面出现呢.1首先找到ecshop目录下的languages\zh_cn\admin/template.php 这个php文件 当然如果这只是简体中 ...

  6. Jmeter系列(17)- 常用断言之JSON断言

    模块分析 Assert JSON Path exists:需要断言的 JSON 表达式 Additionally assert value:如果要根据值去断言,请勾选 Match as regular ...

  7. Xshell和Xftp - 下载安装

    简介 Xshell 实际工作运用:连接Linux Xftp 实际工作运用:传输文件到Linux系统 下载安装 三连后评论区留言私发,此贴长期有效!!!

  8. Shell系列(20)- 字符截取命令cut

    前言 grep是按行提取:cut默认是通过制表符,按列提取,不能识别用空格作为分隔符 语法 cut [选项] [文件] 选项 -f :列号,截取哪几列数据,多个列号用逗号隔开:列与列之间默认用TAB制 ...

  9. ci框架 自定义配置方法

    系统自动在Application文件夹下生成的config.php文件,采用key-value关联数组的形式来存放配置项和值.为了使结构更清晰,手动新建另外一个配置文件myconfig.php,所采用 ...

  10. genymotion启动模拟器后,sdk查询adb devices为空-解决方案

    我们在genymotion中安装了一个安卓模拟器,比如Google Nexus 4,启动该模拟器后,在cmd中输入adb devices,发现为空. 解决方案:在genymotion选择Setting ...