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的更多相关文章

  1. 2016级算法期末模拟练习赛-E.AlvinZH的青春记忆III

    1083 AlvinZH的青春记忆III 思路 难题,二分图. 说这是一个考察二分图的题目,你可以会说"不可能",这哪里像一个二分图了!这真的是一个二分图,考察的是最小顶点覆盖. ...

  2. 2016级算法期末模拟练习赛-B.AlvinZH的青春记忆I

    1083 AlvinZH的青春记忆I 思路 中等题,动态规划. 简化题意,一个环上取数,数不可相邻,取取得数之和最大值. 环不好表示,可以解开变成一列数,那么答案应为下列两种情况较大者. ①:取第一个 ...

  3. 2016级算法期末模拟练习赛-C.AlvinZH的青春记忆II

    1084 AlvinZH的青春记忆II 思路 中等题,二分. 简化题意,一列数字,每秒会自动-1,特殊操作可以使一个数在1s内-k,问这些数都减至0需要多久. 答案肯定在[1,xMax]之间,采用二分 ...

  4. 2016级算法期末模拟练习赛-D.AlvinZH的序列问题

    1111 AlvinZH的序列问题 思路 中等题,动态规划. 简化题意,. 坑点一:二维int数组MLE,明显会超过内存限制,由于\(n\)最大为1e4,那么我们的dp数组最大也是1e4,考虑使用sh ...

  5. 2016级算法期末模拟练习赛-A.wuli51和京导的毕业旅行

    1063 wuli51和京导的毕业旅行 思路 中等题,二分+贪心. 简化题意,将m+1个数字分成n份,ans为这n段中每段数字和的最大值,求ans最小值及其方案. 对于这种求最小的最大值,最常用的方法 ...

  6. 2016级算法期末上机-H.难题·AlvinZH's Fight with DDLs III

    1119 AlvinZH's Fight with DDLs III 思路 难题,最小点覆盖. 分析题意,某一个任务,既可以在笔记本A的 \(a\) 模式下完成,也可以在笔记本B的 \(b\) 模式下 ...

  7. 2016级算法期末上机-D.简单·AlvinZH's Fight with DDLs I

    1117 AlvinZH's Fight with DDLs I 思路 简单题,动态规划. 本题与期末练习赛B题很相似,而且更为简单些.简化问题:在数字序列上取数,不能取相邻的数. DP数组定义,dp ...

  8. 2016级算法期末上机-F.中等·AlvinZH's Fight with DDLs II

    1118 AlvinZH's Fight with DDLs II 思路 中等题,贪心. 理解题意,每次攻击中,可以使某个敌人生命值-1,自己生命值减去∑存活敌人总攻击力. 贪心思想,血量少攻击高的要 ...

  9. 2016级算法第二次上机-E.AlvinZH的儿时梦想——运动员篇

    862-AlvinZH的儿时梦想--运动员篇 思路 难题. 应该想到,不管给出的数据如何,每一个淘汰的人不会对最终答案产生任何影响,所以每次淘汰就把人除掉就可以了,最后剩下的两个人计算它们从开始到相遇 ...

随机推荐

  1. 最大正方形 · Maximal Square

    [抄题]: 在一个二维01矩阵中找到全为1的最大正方形 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 返回 4 [暴力解法]: 时间分析: 空间分析:i j 中保留一 ...

  2. iconv()错误

    //转换字符编码过程中报错,数据会丢失,解决办法:设置第二个参数为gbk//IGNORE $strexport=iconv('UTF-8',"GBK",$strexport); $ ...

  3. windows聚焦图片文件重命名bash脚本

    win10聚焦路径为: %localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalStat ...

  4. 使用delphi 开发多层应用(二十一)使用XE5 RESTClient 直接访问kbmmw 数据库

    delphi XE5 出来了,增加了android 的开发支持,另外增加了一个RESTClient 来支持访问REST 服务器. 这个功能非常强大,可以直接使用非常多的REST 服务器.同时也可以支持 ...

  5. CoreDNS for kubernetes Service Discovery

    一.CoreDNS简介 Kubernetes包括用于服务发现的DNS服务器Kube-DNS. 该DNS服务器利用SkyDNS的库来为Kubernetes pod和服务提供DNS请求.SkyDNS2的作 ...

  6. vc 使窗口置顶 在最前面

    bool SetWindowTop(CWnd* pWnd){ if(!pWnd) {   return false; } if(pWnd->GetExStyle()&WS_EX_TOPM ...

  7. 第二届CCCC赛后感想 2017-04-15 23:56 88人阅读 评论(0) 收藏

    第一次写赛后感想,也不算什么很正规的比赛,不过这次比赛的时间恰好处于思想变化的阶段,留贴纪念. 先谈谈这次比赛,弱校萌新,依靠申请进了总决赛,发现和第一届不一样,缺少了团队奖心中有点缺乏动力,比赛2个 ...

  8. D3 datum()

    datum()和data()功能类似. datum():将指定数据赋值给被选择元素.data():将数据数组与选择集的元素结合. var p = d3.selectAll('p'); p.datum( ...

  9. XXX 不是当前用户的有效责任,请联系您的系统管理员

    EBS中,有时进入一些基于OA Framework 的Web页面时,会出现这种现象: XXX  不是当前用户的有效责任,请联系您的系统管理员 ( or: xxx is not a valid resp ...

  10. Oracle EBS Add Responsibility to User by the Responsibility reference of Other User.

    Oracle EBS 11i Add Responsibility to User by the Responsibility reference of Other User. Warning: R1 ...