AcWing895. 最长上升子序列

方法一

采用从前往后推的方法

#include <bits/stdc++.h>
using namespace std;
#define N 1006
typedef long long ll;
ll a[N];
ll f[N];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", a+i);//注意:我存储的是ll,所以输入也应该是lld
f[0] = 0;//注意:初始条件一定要给
a[0] = INT_MIN;//注意:对于这种有对比的情况,是要更改一下原数组的第一个元素
for(int i = 0; i < n; i++)
for(int j = i+1; j <= n; j++)
if(a[j] > a[i])
{
f[j] = max(f[j], f[i]+1);
}
ll ans = 0;
for(int i = 1; i <= n; i++)//注意:这里的最优解并不是从最后一个元素里面找,而是在1~N中找
{
ans = max(ans, f[i]);
}
cout << ans;
return 0;
}

方法二

思考这一种状态可以由哪些推过来

#include <bits/stdc++.h>
using namespace std;
#define N 1006
typedef long long ll;
ll a[N];
ll f[N];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", a+i);
f[0] = 0;//注意:初始条件一定要给
a[0] = INT_MIN;//注意:对于这种有对比的情况,是要更改一下原数组的第一个元素
for(int i = 1; i <= n; i++)
for(int j = 0; j < i; j ++)
{
if(a[j] < a[i])
{
f[i] = max(f[i], f[j]+1);
}
}
ll ans = 0;
for(int i = 1; i <= n; i++)
{
ans = max(ans, f[i]);
}
cout << ans;
return 0;
}

当询问最长子序列是哪些的时候

#include <bits/stdc++.h>
using namespace std;
#define N 1006
typedef long long ll;
ll a[N];
ll f[N];
ll path[N];
void Print(int i)
{
if(i==0) return;
Print(path[i]);
cout << a[i] << ' ';
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lld", a+i);
f[0] = 0;//注意:初始条件一定要给
a[0] = INT_MIN;//注意:对于这种有对比的情况,是要更改一下原数组的第一个元素
for(int i = 1; i <= n; i++)
for(int j = 0; j < i; j ++)
{
if(a[j] < a[i])
{
if(f[i] < f[j]+1)
{
path[i] = j;//并不需要把之前的全部存起来,只需要知道从哪里转移过来就行了
f[i] = f[j]+1;
}
}
}
ll ans = 0;
ll pos = 0;
for(int i = 1; i <= n; i++)
{
if(f[i] > ans)
{
ans = f[i];
pos = i;
}
}
Print(pos);
puts("");
cout << ans; return 0;
}

896. 最长上升子序列 II

思路

这一道题目不能再使用动态规划来进行求解

必须采用更为快速的

O(NlogN)做法:贪心+二分

a[i]表示第i个数据。

dp[i]表示表示长度为i+1的LIS结尾元素的最小值。

利用贪心的思想,对于一个上升子序列,显然当前最后一个元素越小,越有利于添加新的元素,这样LIS长度自然更长。

因此,我们只需要维护dp数组,其表示的就是长度为i+1的LIS结尾元素的最小值,保证每一位都是最小值,

这样子dp数组的长度就是LIS的长度。

dp数组具体维护过程同样举例讲解更为清晰。

同样对于序列 a(1, 7, 3, 5, 9, 4, 8),dp的变化过程如下:

  • dp[0] = a[0] = 1,长度为1的LIS结尾元素的最小值自然没得挑,就是第一个数。 (dp = {1})
  • 对于a[1]=7,a[1]>dp[0],因此直接添加到dp尾,dp[1]=a[1]。(dp = {1, 7})
  • 对于a[2]=3,dp[0]< a[2]< dp[1],因此a[2]替换dp[1],令dp[1]=a[2],因为长度为2的LIS,结尾元素自然是3好过于7,因为越小这样有利于后续添加新元素。 (dp = {1, 3})
  • 对于a[3]=5,a[3]>dp[1],因此直接添加到dp尾,dp[2]=a[3]。 (dp = {1, 3, 5})
  • 对于a[4]=9,a[4]>dp[2],因此同样直接添加到dp尾,dp[3]=a[9]。 (dp = {1, 3, 5, 9})
  • 对于a[5]=4,dp[1]< a[5]< dp[2],因此a[5]替换值为5的dp[2],因此长度为3的LIS,结尾元素为4会比5好,越小越好嘛。(dp = {1, 3, 4, 9})
  • 对于a[6]=8,dp[2]< a[6]< dp[3],同理a[6]替换值为9的dp[3],道理你懂。 (dp = {1, 3, 5, 8})

这样子dp数组就维护完毕,所求LIS长度就是dp数组长度4。

通过上述求解,可以发现dp数组是单调递增的,因此对于每一个a[i],先判断是否可以直接插入到dp数组尾部,

即比较其与dp数组的最大值即最后一位;如果不可以,则找出dp中第一个大于等于a[i]的位置,用a[i]替换之。

这个过程可以利用二分查找,因此查找时间复杂度为O(logN),所以总的时间复杂度为O(N*logN)

转载自 紫芝 https://blog.csdn.net/qq_40507857/article/details/81198662 CSDN[1]

这道题目恰恰使用到了贪心的思想。

dp数组里面存放了(当前当前位置之前,所有)长度为 i 的上升子序列的最小的末尾元素(只需要一个代表元,那就是结尾的元素)

依次从左向右进行扫描,对比 a[ i ] 和dp数组里面的值。

  1. 如果a[i]的值大于dp数组的最后一个值,那么由于a[i]的加入,就会让最长子序列长度增加。
  2. 如果a[i]的值小于等于dp数组的最后一个元素,找一个大于等于a[i]的第一个元素的下标p。

    p之前的数字全部比 a 小,a 可以替换p的位置,完全合法,并且让p的位置的数字更小,更可能出现较长的上升子序列。

代码

#include <bits/stdc++.h>
using namespace std;
#define N 100020
int a[N], dp[N];
int m = 0; int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", a+i);
dp[1] = a[1];
m = 1;
for(int i = 2; i <= n; i++)
{
if(a[i] > dp[m])
{
dp[++m] = a[i];
continue;
}
int p = lower_bound(dp+1, dp+1+m, a[i])-(dp+1)+1;
dp[p] = a[i];
}
printf("%d", m);
return 0;
}

竟然更加简单:)

AcWing\897. 最长公共子序列

思路

闫氏DP分析法

  1. 状态:dp[i][j]代表在A序列里面以i结尾,在B中j结尾的序列。

    • 子结构:A中以i结尾,在B中j结尾的序列中所有的公共子序列。
    • 最优子结构:最长的公共子序列
    • 状态表示:使用一个整数,表示长度。
  2. 转移:

    对于dp[i][j]一共有以下四种情况:

    • 包含a[i],包含b[j]

      注意有前提条件:a[i] == b[j]

      这时候状态表示是dp[i-1][j-1]+1

    • 包含a[i],不包含b[j]

      没有办法求得这种情况的等价情况。

      但是有一个集合可以满足要求:dp[i][j-1]这一个集合有两种情况

      1. 包含a[i],不包含b[j]
      2. 不包含a[i],不包含b[j]

      这是因为我的状态表示仅仅说明是a[1~i]b[1~j]的最长的公共子序列,包不包含a[i]我并不知道。

    • 不包含a[i],包含b[j]; 和上面一致!

    • 不包含a[i],不包含b[j]。 显然是dp[i-1][j-1]

由于有重复情况,所以最终dp方式见代码

代码

#include <bits/stdc++.h>
using namespace std;
#define N 1020
char a[N], b[N];
int dp[N][N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", a+1);
scanf("%s", b+1);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
if(a[i] == b[j])
dp[i][j] = max(dp[i-1][j-1]+1, dp[i][j]);
}
}
printf("%d", dp[n][m]);
return 0;
}

AcWing898. 数字三角形

输入样例:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

输出样例:

30

思路

这一道题目给定的三角不便于存储,要想办法改变一下。我们可以使用样例中的办法进行存储

这样,对应的规则就变成了向下或者向右下方走。

#include <bits/stdc++.h>
using namespace std;
#define N 506
int a[N][N];
int dp[N][N];
#define WAY2
//更换上面这一个定义,可以使用两种方法!
int main()
{ int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= i; j++)
dp[i][j] = -0x3f3f3f3f;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= i; j++)
scanf("%d", &a[i][j]); #ifdef WAY1
dp[1][1] = a[1][1];
for(int i = 1; i < n; i++)
for(int j = 1; j <= i; j++)
{
dp[i+1][j] = max(dp[i+1][j], dp[i][j]+a[i+1][j]);
dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j]+a[i+1][j+1]);
}
#endif #ifdef WAY2
dp[1][1] = a[1][1];
for (int i = 2; i <= n; i++)
for(int j = 1; j <= i; j++)
{
if(j > 1)dp[i][j] = max(dp[i][j], dp[i-1][j-1] + a[i][j]);//注意两个边界条件的判断
if(j< i ) dp[i][j] = max(dp[i][j], dp[i-1][j]+a[i][j]);
}
#endif int ans = -0x3f3f3f3f;
for(int i = 1; i<= n; i++)
{
ans = max(ans, dp[n][i]);
}
printf("%d", ans);
return 0;
}

参考资料


  1. 转载自 紫芝 https://blog.csdn.net/qq_40507857/article/details/81198662 CSDN

最新文章

  1. Codeforces #261 D
  2. Asp.net上传文件后台通过二进制流发送到其他Url保存
  3. 利息?hii
  4. 【图像处理】【SEED-VPM】1.板子基本操作流程
  5. [算法] [常微分方程] [欧拉法 改进欧拉法 经典R-K算法]
  6. SQL 统计表行数和空间大小
  7. Hive Experiment 2(表动态分区和IDE)
  8. Verilog学习笔记基本语法篇(九)&#183;&#183;&#183;&#183;&#183;&#183;&#183;&#183; 任务和函数
  9. 使用JMeter创建FTP测试计划
  10. windows下搭建svn服务端、客户端
  11. 《HTML5与CSS3基础教程》学习笔记 ——One Day
  12. 使用kdbg或nemiver调试ROS
  13. 天涯html&amp;css基础框架
  14. 打破C++ Const 的规则
  15. web移动开发的小细节(持续添加)
  16. 【转】emulator: ERROR: Could not load OpenGLES emulation library: lib64OpenglRender.so
  17. 运维自动化之SALTSTACK简单入门
  18. IO (一)
  19. Docker学习笔记 - Docker客户端和服务端
  20. python--元祖和字典

热门文章

  1. Docker系列教程01-使用Docker镜像
  2. Docker的基本原理及使用
  3. python之三元表达式与生成式与匿名与内置函数(部分)
  4. C++从静态类型到单例模式
  5. Codeforces Round #746
  6. CF335E Counting Skyscrapers 题解
  7. nginx 代理请求导出功能bug解决方法
  8. 使用kubeseal加密和管理k8s集群的secret
  9. 将 Ubuntu 16.04 LTS 的 Unity 启动器移动到桌面底部命令
  10. JS:Array