今天主要学习了线性DP和背包问题以及快速幂。
1.整数快速幂:
这个直接粘上代码
int QPow(int x,int n)
{
int res = x,ans = 1;
while(n)
{
if(n&1)
{
ans = ans * res;
}
res = res*res;
n = n>>1;
}
return ans;
}
2.矩阵快速幂:
主要就是将整数快速幂的乘法运算换做矩阵的乘法
下面的代码是方阵的快速幂
const int N=10;
int tmp[N][N];
void multi(int a[][N],int b[][N],int n)//n为方阵的大小
{
memset(tmp,0,sizeof tmp);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
for(int k=0;k<n;k++)
tmp[i][j]+=a[i][k]*b[k][j];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
a[i][j]=tmp[i][j];
}
int e[N][N];
void Pow(int a[][N],int n)
{
memset(e,0,sizeof e);//n是幂,N是矩阵大小
for(int i=0;i<N;i++) e[i][i]=1; //e[][]为单位矩阵
while(n)
{
if(n&1)
multi(e,a,N);
multi(a,a,N);//a=a*a
n>>=1;
}
}
矩阵快速幂的一个重要应用就是求递推式,这个在这里不做延伸。
3.线性DP
最长不下降子序列的长度:
用dp[i]表示前i个元素的LIS
对于第i个元素,其所对应的长度应从前往后找,找到所有可以添加的子序列,并取其中的最大值,可知状态转移方程:
dp[i]=max(dp[i],dp[j]+1); // j:由1到i
最大连续子段和:
用dp[i]表示以第i个元素为结尾的最大连续子段和
对于第i个元素,其dp[i-1] 已经最优,那么这个元素要么加上前面的最优,要么本身就是最优,可知状态转移方程:
dp[i]=max(a[i],dp[i-1]+a[i]);// 然后再遍历求出最大值即可
最长公共子序列:
假设两个子序列x[1~m],y[1~n] 且z[1~k]为它们的最长公共子序列,则有:
如果xm=yn,则zk=am=bn(只有这样子序列才最长),且z[1~k-1]是x[1~m-1]和y[1~n-1]的一个最长公共子序列;
如果xm!=yn,若zk!=xm,则z[1~k]是x[1~m-1]和y[1~n]的一个最长公共子序列;若zk!=bn,则z[1~k]是x[1~m]和y[1~n-1]的一个最长公共子序列。可知状态转移方程:
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j])
{
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
4.一些思想
1)先看这样一道题:
假设我们有一个序列S,我可以从中取出若干的数,并且这些序列还满足下面的条件:
1.取出负数的话,我不得钱
2.取出S[i]>=10000的数,我可以得到5块钱,并且我要把他变成 S[i] = S[i] – 10000
3.其余情况我只能得到一块钱
4.取出的数不能打乱原有的顺序
问我能不能选出这样一个子序列t,使得t中的元素都是不下降的,并且得到钱最多?
这是一个最长不下降子序列问题,对于大于一万的数怎么处理呢?
关键来了,可以将大于一万的数减去一万,然后将该数变为五个插入到当前数所在位置(这样处理,每个数的价值就是一,这个数只要选一个就可以选五个,价值就是五)
2)二维前缀和:
采用二阶矩阵来记录矩阵某一点(m,n)和(0,0)所在行列所围成的矩阵中所有数的和,扫一遍即可求出这个二阶矩阵。