线性规划
例题
先看一道 I O I IOI IOI 原题 :
IOI1994数字三角形 Number Triangles–洛谷
具体题目描述见题目
状态表达: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从顶部走到点 ( i , j ) (i,j) (i,j) 经过的数字最大值。
状态转移方程:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) dp[i][j]=max(dp[i-1][j-1],dp[i-1][j]) dp[i][j]=max(dp[i−1][j−1],dp[i−1][j])
初值(边界条件) : d p [ 1 ] [ 1 ] = a [ 1 ] [ 1 ] dp[1][1]=a[1][1] dp[1][1]=a[1][1]
目标 : m a x ( d p [ n ] [ j ] ) max(dp[n][j]) max(dp[n][j]) j ∈ [ 1 , n ] j∈[1,n] j∈[1,n]
D P DP DP 代码:
#include<bits/stdc++.h>
using namespace std;
int dp[1024][1024];
int a[1024][1024],m;
int solve_1(int n){
int ans=0;
dp[1][1]=a[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
dp[i][j]=a[i][j]+max(dp[i-1][j],dp[i-1][j-1]);
for(int i=1;i<=n;i++)ans=max(ans,dp[n][i]);
return ans;
}
int dp_1[10005];
int solve_2(int n){
int ans=0;
dp_1[1]=a[1][1];
for(int i=2;i<=n;i++){
for(int j=i;j>=1;j--){
dp_1[j]=max(dp_1[j],dp_1[j-1])+a[i][j];
}
}
for(int i=1;i<=n;i++){
ans=max(ans,dp_1[i]);
}
return ans;
}
int main(){
scanf("%d",&m);
for(int i=1;i<=m;i++){
for(int j=1;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
printf("%d",solve_2(m));
return 0;
}
学习秘籍
( 1 ) (1) (1) 为什么这么做?
多想一下
( 2 ) (2) (2) 想不出状态转移方程怎么办?
多问一句
( 3 ) (3) (3) 举一反三,拓展思维
需要有尝试的勇气,打开思维。
最长上升子序列
例题
子序列 : 只能从前往后取。
如对这样一个序列 ( 1 , 3 , 5 , 9 , 7 , 8 , 4 ) (1,3,5,9,7,8,4) (1,3,5,9,7,8,4)
它的子序列就有
( 1 , 5 , 9 , 8 , 4 ) (1,5,9,8,4) (1,5,9,8,4) 当然,这不是上升子序列。
像 ( 1 , 3 , 5 , 7 , 8 ) (1,3,5,7,8) (1,3,5,7,8) 这样的子序列才叫上升子序列。
简而言之,就是子序列中的元素必须满足单调递增。
现在给你一个数列,请你输出他的最长的子序列的长度。
输入 :
7
1 7 3 5 9 4 8
输出 :
4
求解过程
( 1 ) (1) (1) 确定状态: d p [ i ] dp[i] dp[i] 表示以 a [ i ] a[i] a[i] 结尾的最长上升子序列长度。
( 2 ) (2) (2) 状态转移: 对于 1 ≤ j < i 1\le j<i 1≤j<i , 若 a [ j ] < a [ i ] a[j] < a[i] a[j]<a[i] , 则可以将 a [ i ] a[i] a[i] 放在以 a [ i ] a[i] a[i] 结尾的最长上升序列后面,得到的长度为 d p [ j ] + 1 dp[j]+1 dp[j]+1 。
( 3 ) (3) (3) 边界条件: d p [ 0 ] = 0 dp[0]=0 dp[0]=0
( 4 ) (4) (4) 求解目标: m a x ( d p [ i ] ) max(dp[i]) max(dp[i])
状态转移方程:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) , 1 ≤ j < i dp[i]=max(dp[i],dp[j]+1), 1 \le j <i dp[i]=max(dp[i],dp[j]+1),1≤j<i
时间复杂度 O ( n 2 ) O(n^2) O(n2)
代码实现:
#include<cstdio>
#include<iostream>
using namespace std;
int len,dp[100005],num[100005],ans;
int main(){
scanf("%d",&len);
for(int i=1;i<=len;i++)scanf("%d",&num[i]);
for(int i=1;i<=len;i++){
dp[i]=1;
for(int j=1;j<i;j++){
if(num[i]>num[j])dp[i]=max(dp[i],dp[j]+1);
}
}
for(int i=1;i<=len;i++)ans=max(ans,dp[i]);
printf("%d",ans);
return 0;
}
设置一个辅助数组 d [ l e n ] d[len] d[len]
d d d 数组 表示最长上升子序列, d [ i ] d[i] d[i] 表示最长上升子序列的第 i i i 项。(其实也不一定是,不信你试试)
输入
5
1 8 9 10 2
不过输出到是正确的……
我们要符合这样一个规则:
1. 1. 1. 将 n u m [ i ] num[i] num[i] 和 d [ l e n ] d[len] d[len] 进行比较
( 1 ) (1) (1) 若 n u m [ i ] > d [ l e n ] num[i]>d[len] num[i]>d[len] 则将 n u m [ i ] num[i] num[i] 放到 d [ l e n ] d[len] d[len] 的后面。
( 2 ) (2) (2) 若 n u m [ i ] < d [ l e n ] num[i]<d[len] num[i]<d[len] 则将 n u m [ i ] num[i] num[i] 替换为 第一个大于等于 d [ l e n ] d[len] d[len]
时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
代码:
#include<bits/stdc++.h>
using namespace std;
int d[10005],a[10005];
int k,n,maxx,z,len=1;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
d[1]=a[1];
for(int i=2;i<=n;i++){
if(a[i]==d[len])continue;
if(a[i]>d[len])d[++len]=a[i];
else{
//*表示内容
*lower_bound(d+1,d+len+1,a[i])=a[i];
}
}
printf("%d",len);
return 0;
}
大家点一下关注吧!也可以点一个免费的赞,谢谢啦!