2016级算法期末模拟练习赛-F.AlvinZH的青春记忆IV
1086 AlvinZH的青春记忆IV
思路
难题,动态规划。
这是一道很有意思的题,因为它不仅卡了时间,也卡了空间,而且卡的很妙很迷。
光是理解题意已经有点难度,简化题意:两串数字序列,相等的数字定义为可以交战,若 AiBj,则 Ai 可与 Bj 交战,之后下一场 Aii、Bjj 交战的条件是:AiiBjj、ii>i、jj>j、Aii>Ai、Bjj>Bj 。
这是什么呢?最长公共子序列?最长递增子序列?都很像,其实是最长公共递增子序列!最长公共子序列与最长递增子序列的叠加,即LCS+LIS,称为 LCIS 问题。
如果你不知道怎么求LCS和LIS,请复习后继续往下看。
在开始讲TLE问题前,先讲一下MLE问题。可以发现题目中所给的 \(n\) 最大有1e4,如果你的\(dp\)数组是int的二维1e4数组,明显已经超过空间限制。
这里想一想 \(dp\) 数组的定义,它代表的是LCIS的大小,这个大小是不可能超过 \(n\) 和 \(m\) 的,所以dp[i][j]<1e4,所以dp数组定义为 \(short\ int\)。
注意一个小问题:数学函数max(int,int),比较大小前需要转成int。
方法一:最原始的方法——完全暴力
\(dp[i][j]\):A前i个元素和B前j个元素最长公共上升子序列。
四重for循环,外层两个判断LCS,内层两个判断LIS。TLE,不用说。
代码如下,时间复杂度:O(n4),空间复杂度:O(n2)。可得0.4分,TLE。
int n, m, ans;
int A[10005];
int B[10005];
short int dp[10005][10005];//dp[i][j]:A前i个元素和B前j个元素最长公共上升子序列
ans = 0;
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(A[i] == B[j])
{
for(int k = 0; k < i; k++)
if(A[k] < A[i])
for(int l = 0;l < j; l++)
dp[i][j] = max((int)dp[i][j], (int)dp[k][l]+1);
}
ans = max(ans, (int)dp[i][j]);
}
}
printf("%d\n", ans);
//完全暴力
//时间复杂度:O(n^4)
//空间复杂度:O(n^2)
方法二:推翻重来,重新定义DP数组
可以发现,上一种方法中DP数组定义实在是太普通了,我们可以对其加以限制。
\(dp[i][j]\):以A串的前i个整数与B串的前j个整数且以B[j]为结尾构成的LCIS的长度,即不强制选择A[i]且强制选择B[j]的长度。
状态转移方程:
- ①dp[i][j] = dp[i-1][j]。(A[i] != B[j])
- ②dp[i][j] = max(dp[i-1][k])+1 (A[i] == B[j]) (1 <= k <= j-1 && B[j] > B[k])
对于①,dp[i][j]定义可知:B[j]必须使用。但A[i] != B[j],A[i]无贡献,可直接取dp[i-1][j]。
对于②:两数相等情况下为了最长,B[j]一定与A[i]匹配,若与之前某A[1i-1]匹配,一定没有比与A[i]匹配更优。接下来需要去找一个递增的以B[j]结尾的LCIS,枚举dp[i-1][1j-1]找最大值。为什么第一维是i-1,A[i]已经匹配,而取[1~i-2]显然没有比取i-1更优。
参考代码一:最浅显的翻译
根据上述dp定义可以直接翻译,得到最浅显的解决代码如下,依然超时,可得0.4分, TLE。
时间复杂度:O(n3),空间复杂度:O(n2)。
int n, m, ans;
int A[10005];
int B[10005];
short int dp[10005][10005];//dp[i][j]:以A串的前i个整数与B串的前j个整数且以B[j]为结尾构成的LCIS的长度
ans = 0;
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(A[i] == B[j])
{
int maxx = 0;
for(int k = 1; k <= j-1; k++)//找到符合递增的最大长度
{
if(B[j] > B[k])//保证递增
maxx = max(maxx, (int)dp[i-1][k]);
}
dp[i][j] = maxx + 1;
}
else
dp[i][j] = dp[i-1][j];
}
}
for(int j = 1; j <= m; j++)
if(ans < dp[n][j]) ans = dp[n][j];
//时间复杂度:O(n^3)
//空间复杂度:O(n^2)
参考代码二:代码一的时间优化
时间优化:maxx可以在第二层循环直接求得,不需要第三层循环。如何求得?
首先明白maxx的定义:maxx=max(dp[i-1][k]),k=[1,j-1]。指的是符合递增条件下(B[k]<B[j]),A[1~i-1]与B[1,j-1]形成的LCIS。
由于B[j]已经与A[i]匹配,要找的是B[j] > B[k],即推出A[i] > B[k]。在第二层循环中直接判断即可更新maxx的值。
代码如下,时间复杂度:O(n2),空间复杂度:O(n2)。可得0.6分,TLE。
int n, m, ans;
int A[10005];
int B[10005];
short int dp[10005][10005];//dp[i][j]:以A串的前i个整数与B串的前j个整数且以B[j]为结尾构成的LCIS的长度
ans = 0;
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
int maxx = 0;
for(int j = 1; j <= m; j++)
{
if(A[i] == B[j])
dp[i][j] = maxx+1;
else
dp[i][j] = dp[i-1][j];
if(A[i] > B[j])
maxx = max(maxx, (int)dp[i-1][j]);
}
}
for(int j = 1; j <= m; j++)
if(ans < dp[n][j]) ans = dp[n][j];
//时间复杂度:O(n^2)
//空间复杂度:O(n^2)
参考代码三:对代码二的空间优化
发现dp更新时只与上一状态有关,所以dp数组可以变成dp[2][10005],位操作滚动更新,其他不变。
代码如下,时间复杂度:O(n^2),空间复杂度:O(n)。可得1分,AC。终于可以AC了,但是,为什么呢?
莫名AC了,应该问问为什么。代码二与代码三对比,只是空间比小了,对于第二组数据,为什么一个1500ms直接T,而这个36ms刷的过去了呢?第二组数据有什么坑?
我直说:第二组数据中n很小,Ai和Bi很小,不过数据组数有点大。一个原因可以理解为初始化问题,第二个原因是堆空间分配问题,看你们计组学的如何了!
int n, m, ans;
int A[10005];
int B[10005];
short int dp[2][10005];
ans = 0;
memset(dp, 0, sizeof(dp));
int cur = 0;
for(int i = 1; i <= n; i++)
{
cur = cur^1;
int maxx = 0;
for(int j = 1; j <= m; j++)
{
if(A[i] == B[j])
dp[cur][j] = maxx+1;
else
dp[cur][j] = dp[cur^1][j];
if(A[i] > B[j])
maxx = max(maxx, (int)dp[cur^1][j]);
}
}
for(int j = 1; j <= m; j++)
if(ans < dp[cur][j]) ans = dp[cur][j];
printf("%d\n", ans);
//时间复杂度:O(n^2)
//空间复杂度:O(n)
参考代码四:对代码三进一步优化
什么!!!还可以优化!
是的,其实既然可以位操作滚动优化,有可能可以像01背包一样直接变为一维数组。这里确实可以这样做。
dp数组的定义稍微有所改变,dp[i]表示以B[i]结尾的LCIS,A的范围是[1~n]。
代码如下,时间复杂度:O(n^2),空间复杂度:O(n)。可得1分,AC。
int n, m, ans;
int A[10005];
int B[10005];
short int dp[10005];//dp[i]表示以B[i]结尾的LCIS,A的范围是[1~n]
ans = 0;
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
int maxx = 0;
for(int j = 1; j <= m; j++)
{
if(A[i] == B[j])
dp[j] = maxx+1;
if(A[i] > B[j])
maxx = max(maxx, (int)dp[j]);
}
}
for(int j = 1; j <= m; j++)
if(ans < dp[j]) ans = dp[j];
//时间复杂度:O(n^2)
//空间复杂度:O(n)
分析
不分析了,还分析个毛线!只能说,我们对DP的力量一无所知!
2016级算法期末模拟练习赛-F.AlvinZH的青春记忆IV的更多相关文章
- 2016级算法期末模拟练习赛-E.AlvinZH的青春记忆III
1083 AlvinZH的青春记忆III 思路 难题,二分图. 说这是一个考察二分图的题目,你可以会说"不可能",这哪里像一个二分图了!这真的是一个二分图,考察的是最小顶点覆盖. ...
- 2016级算法期末模拟练习赛-B.AlvinZH的青春记忆I
1083 AlvinZH的青春记忆I 思路 中等题,动态规划. 简化题意,一个环上取数,数不可相邻,取取得数之和最大值. 环不好表示,可以解开变成一列数,那么答案应为下列两种情况较大者. ①:取第一个 ...
- 2016级算法期末模拟练习赛-C.AlvinZH的青春记忆II
1084 AlvinZH的青春记忆II 思路 中等题,二分. 简化题意,一列数字,每秒会自动-1,特殊操作可以使一个数在1s内-k,问这些数都减至0需要多久. 答案肯定在[1,xMax]之间,采用二分 ...
- 2016级算法期末模拟练习赛-D.AlvinZH的序列问题
1111 AlvinZH的序列问题 思路 中等题,动态规划. 简化题意,. 坑点一:二维int数组MLE,明显会超过内存限制,由于\(n\)最大为1e4,那么我们的dp数组最大也是1e4,考虑使用sh ...
- 2016级算法期末模拟练习赛-A.wuli51和京导的毕业旅行
1063 wuli51和京导的毕业旅行 思路 中等题,二分+贪心. 简化题意,将m+1个数字分成n份,ans为这n段中每段数字和的最大值,求ans最小值及其方案. 对于这种求最小的最大值,最常用的方法 ...
- 2016级算法期末上机-H.难题·AlvinZH's Fight with DDLs III
1119 AlvinZH's Fight with DDLs III 思路 难题,最小点覆盖. 分析题意,某一个任务,既可以在笔记本A的 \(a\) 模式下完成,也可以在笔记本B的 \(b\) 模式下 ...
- 2016级算法期末上机-D.简单·AlvinZH's Fight with DDLs I
1117 AlvinZH's Fight with DDLs I 思路 简单题,动态规划. 本题与期末练习赛B题很相似,而且更为简单些.简化问题:在数字序列上取数,不能取相邻的数. DP数组定义,dp ...
- 2016级算法期末上机-F.中等·AlvinZH's Fight with DDLs II
1118 AlvinZH's Fight with DDLs II 思路 中等题,贪心. 理解题意,每次攻击中,可以使某个敌人生命值-1,自己生命值减去∑存活敌人总攻击力. 贪心思想,血量少攻击高的要 ...
- 2016级算法第二次上机-E.AlvinZH的儿时梦想——运动员篇
862-AlvinZH的儿时梦想--运动员篇 思路 难题. 应该想到,不管给出的数据如何,每一个淘汰的人不会对最终答案产生任何影响,所以每次淘汰就把人除掉就可以了,最后剩下的两个人计算它们从开始到相遇 ...
随机推荐
- Error while trying to retrieve text for error ORA-12154
问题描述:vs中调试运行没有任何错误,但是发布到IIS中访问,就会报以上错误.IIS不会调试,所以一头雾水,不止错误在哪里. 分析:看到网上有人分析了Web.config模拟验证的问题恍然大悟: 原文 ...
- sql产生随机数字
第一种:select cast(ceiling(rand() * 10) as int)第二种:select cast(ceiling(rand(checksum(newid()))*10) as i ...
- golang之数组
1.数组:同一种数据类型的固定长度的序列. 2.数组定义:var a [len]int,例如:var a [5]int 3.长度是数组类型的一部分,因此,var a[5] int 和 var a[10 ...
- 利率计算v4.0--测试--软件工程
利率计算v4.0--测试 package Test; import Model.Interest; import Service.CompoundInterestService; import Ser ...
- advance shading——基础(辐射度测定)
辐射度测定(radiometry) <real time rendering>在这章上来就说了一大堆光照方面的物理术语,不知该怎么翻译.后来在维基百科上看到这个表,清楚了很多(这里的w是瓦 ...
- [模板]tarjan缩点+拓扑排序
题目:给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 题目简述:先t ...
- Java Thread系列(二)线程状态
Java Thread系列(二)线程状态 一.线程的五种状态 新建状态(New):新创建了一个线程对象,尚未启动. 就绪状态(Runnable):也叫可运行状态.线程对象创建后,其他线程调用了该对象的 ...
- 2018年3大UI设计趋势,你知道吗?
以下内容由Mockplus团队翻译整理,仅供学习交流,Mockplus是更快更简单的原型设计工具. 之前小编已经和大家讨论了2018年软件测试的五大趋势,现在让我们一起来看看移动UI设计在2018年会 ...
- rabbitmq用户权限管理
原文地址: http://my.oschina.net/hncscwc/blog/262246 安装最新版本的rabbitmq(3.3.1),并启用management plugin后,使用默认的账号 ...
- PL/SQL Developer 窥探事务
一次登录代表一个连接 一个SQL Window 代表一个会话(session),有唯一的SID 事务(transaction) 由 insert .update 或者 delete 开启 由 comm ...