线性指的是状态的排布是线性的,最长单调上升子序列为经典的线性模型
leetcode300:最长递增子序列
用f[i]表示以a[i]结尾的最长上升子序列问题
计算f[i]的时候,只需要考虑f[1],f[2],...,f[i-1]是否能够转移
状态转移方程:f[i]=max{f[j]+1|1<=j<i,a[i]<a[j]} ,并将此集合称为决策允许集合,我们只能从允许的j里找f[j]+1来更新f[i].
代码:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len=nums.size();
int f[len];
for(int i=0;i<len;i++)
f[i]=1;
int maxlen=1;
for(int i=1;i<len;i++)
for(int j=0;j<=i-1;j++)
{
if(nums[i]>nums[j])
f[i]=max(f[i],f[j]+1);
maxlen=maxlen<f[i]?f[i]:maxlen;
}
return maxlen;
}
};
poj3086
题目大意:
想保证n年里都有一台电脑,一开始你有n台,如果在第y年购买了一台电脑,那么你需要花费c的代价。
如果那台电脑一直用到了第z年,在第z年又买了一台新的,你需要支付m(y,z)的维修费用
给定n,c,数组m。求最小花费。
m(x年开始买入,用到了y年年末)
题解:我们将每年划分为一个阶段
f[i]表示直到第i年都有一台电脑的最小花费。
f[i]需要从f[0],f[1],...,f[i-1]转移过来。
对于第i年的最小花费,我们依次设置变量从1到i依次枚举上次买电脑是哪一年。
假设上次买电脑是第j年,那么1~j-1年就是一个子问题,我们已经求出了f[j-1]是满足此问题的最优解,后面就不用考虑前j-1年的情况,且它也不会影响我们对后面的决策。
第j年到第i年的维修费用是m(j,i),花费为c
因此可以用f[j-1]+m(j,i)+c来更新f[i]
f[i]=min{f[i],f[j-1]+m(j,i)+c} (1<=j<=i)
代码实现:
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
f[i]=min{f[i],f[j-1]+m[j][i]+c};
printf("%d\n",f[n]);
时间复杂度O(n^2)
为了增加对无后效性的理解,我们以本题的数据为例,给出每次枚举过程:
一:
i=1,j=1:f[1]=min(f[1],f[0]+m(1,1)+c)=8
二:
i=2,j=1:f[2]=min(f[2],f[0]+m(1,2)+c)=3+7=10
i=2,j=2:f[2]=min(f[2],f[1]+m(2,2)+c)=min(10,8+6+3)=10
三:
i=3,j=1:f[3]=min(f[3],f[0]+m(1,3)+c)=50+3=53
i=3,j=2:f[3]=min(f[3],f[1]+m(2,3)+c)=min(53,8+8+3)=19
i=3,j=3:f[3]=min(f[3],f[2]+m(3,3)+c)=min(19,10+10+3)=19
可以看到在i=3循环内,每次枚举所用到的f[1],f[2],都是之前求出的最优解,直接用就可以。
至于为什么min里面要用f[j-1],是因为对于f[i]=min{f[i],f[j-1]+m[j][i]+c}为i年的最小花费,
若上次买电脑的年份为第j年,那么第(j~i)年的费用总和为第j年买电脑的费用c加上之后的维修费
m(j,i),只需要加上前j-1年的总花费就可以了。