今天开始学习动态规划了呢/2020/4/3
动态规划真是简单呢(战术麻痹自己)
一、数字三角形/2020/4/3
P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles
应该是最简单的dp题目了,从下往上滚
dp[i][j]等于脚下两个dp的最大值加上a[i][j]
#include <bits/stdc++.h>
using namespace std;
int w[500],c[500],dp[1005][1005],flag[500];
int tong[500];
int v,n;
int a[1005][1005];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
}
for(int i=n;i>=1;i--)
for(int j=1;j<=i;j++)
{
dp[i][j]=(max(dp[i+1][j],dp[i+1][j+1])+a[i][j]);
}
cout<<dp[1][1]<<endl;
return 0;
}
二、最长上升子序列问题/2020/4/3
描述:
比如
1,7,3,5,8,4,8
问你最长的递增的子序列的长度是多少
这个问题的最优解有多个,但是最优值只有一个:长度为4
n^2算法
算法描述:dp[x]代表以x为尾的最长子序列的长度,那么dp[x+1]的值便可由dp[x]推来dp[x+1]的方程详见代码注释
蒻蒟的感悟:分析问题要用递归的思维去想,就是找一个实例,看这个dp与它的上一状态dp的关系
要明确dp所代表的的意义,比如这个题就是:表示以下标代表的数字为尾的子序列长度
#include <bits/stdc++.h>
using namespace std;
int a[1000],dp[1000];//dp表示以x为尾的最长子序列的长度
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
dp[1]=1;
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
{
//当a[i]大于a[j]时,那么a[i]便大于dp[j]代表的子序列的最大值
//那么a[i]加上dp[j]代表的子序列的长度便是dp[j]+1
//所以dp[i]就是这些长度(1加上子序列长度)的最大的那一个
if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
}
}
int ma=dp[1];
//因为选的是最长,以最后一个数字为尾的子序列长度并不一定是最长的,所以求出以任意数字结尾的子序列长度最大的那一个
for(int i=1;i<=n;i++)
ma=max(ma,dp[i]);
cout<<ma<<endl;
return 0;
}
三、最长公共子序列问题(LCS)/2020/4/4
问题概述:给你两个序列,问你他们的最长LCS序列的长度是多少?(序列可以是不连续的,只要元素的相对位置一样)(不了解LCS问题的自行百度)
太开心了,这道题没看题解自己就做出来了,第一道完全自己推出状态转移方程的dp题,我tql,给自己orz(一眼就会做的dalao见笑了)
蒻蒟的感悟:先画一张(n+1)*(m+1)的表,更好找出规律,写出方程后也好验证
思路:dp[i][j]表示两个串前n,m个字符的最长公共序列,那么n+1 或m+1时公共序列长度也必定大于n,m的,所以继承二者最大的一个,然后当a[i]==a[j]时,公共长度+1(详见代码)
#include <bits/stdc++.h>
using namespace std;
int dp[1000][1005];//dp表示以x为尾的最长子序列的长度
string a,b;
int main()
{
int n,m;
cin>>n>>m;
cin>>a>>b;
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]++;
}
cout<<dp[n][m]<<endl;
return 0;
}
四、矩阵连乘问题/2020/4/4
问题描述
给定n个矩阵:A1,A2,…,An,其中Ai与Ai+1是可乘的,i=1,2…,n-1。确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。输入数据为矩阵个数和每个矩阵规模,输出结果为计算矩阵连乘积的计算次序和最少数乘次数。
算法描述
下标表示第几个矩阵
dp[i][j]表示把第i个矩阵到第j个矩阵相乘后的数乘次数
最外层循环控制矩阵长度1,2,3
中层循环推进矩阵相乘,dp[1][2],dp[2][3]
内层循环枚举两个矩阵相乘时的分割点,取其最小值
#include <bits/stdc++.h>
using namespace std;
#define INF 99999999
int dp[1000][1005];
int a[1005];
int main()
{
int n;
cin>>n;
for(int i=0;i<=n;i++)cin>>a[i];
for(int len=1;len<=n;len++)//
for(int i=1,j=i+len;j<=n;j++,i++)
{
int mi=INF;
for(int k=i;k<j;k++)
{
//两个矩阵合并时新矩阵的数乘次数
dp[i][j]=dp[i][k]+dp[k+1][j]+a[i-1]*a[k]*a[j];
mi=min(mi,dp[i][j]);
}
dp[i][j]=mi;
}
cout<<dp[1][n]<<endl;
return 0;
}