到底什么是dp思想(内含大量经典例题,附带详细解析)
期末了,通过写博客的方式复习一下dp,把自己理解的dp思想通过样例全部说出来
说说我所理解的dp思想
dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个
最好的决策序列使得这个问题有最优解
将待求解的问题分为若干个相互联系的子问题,只在第一次遇到的时候求解,然后将这个子问题的答案保存
下来,下次又遇到的时候直接拿过来用即可
dp和分治的不同之处在于分治分解而成的子问题必须没有联系(有联系的话就包含大量重复的子问题,那
么这个问题就不适宜分治,虽然分治也能解决,但是时间复杂度太大,不划算),所以用dp的问题和用分
治的问题的根本区别在于分解成的子问题之间有没有联系,这些子问题有没有重叠,即有没有重复子问题
dp和贪心的不同之处在于每一次的贪心都是做出不可撤回的决策(即每次局部最优),而在dp中还有考察
每个最优决策子序列中是否包含最优决策子序列,贪心中每一步都只顾眼前
最优,并且当前的选择是不会依赖以前的选择的,而dp,在选择的时候是从以前求出的若干个与本步骤
相关的子问题中选最优的那个,加上这一步的值来构成这一步那个子问题的最优解
讲得再多不如看几个很经典的样例,带你初步入门dp
我不会讲很多具体该怎么做,而是剖析这些经典例题中的dp思想,真真正正的懂得了dp思想的话,做题事
半功倍(自己深有体会)
样例1:数字三角形问题
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从顶部向下走,每次只能走下面或者右下,走完全程,问你怎么走使得权值最大(问题描述不是很详细,关
于数字三角形问题是什么问题请百度)
那么dp的思想到底是怎么体现的呢?
dp是要先分解成很多相互联系的子问题,要解决一个子问题,依赖于前面和此子问题相关的已经解决的子
问题中选一个最优的加上这个子问题的解,就是这个子问题的最优解
具体做法:
1.分析问题的最优解,找出最优解的性质,并刻画其结构特征:
问题的最优解:所有走法中最大的权值是多少?
最优解的性质和结构特征:只能向正下或者右下走,每走一行的最大权值等于前面一行的最大权值加上这一
行的走的两个方向中的最大值
2.递归的定义最优值:
要找到从0行出发的最优值,就要找到从第1行出发的最优值
要找到从1行出发的最优值,就要找到从第2行出发的最优值
………………………
要找到第3行出发的最优值,就要找到从最后一行出发的最优值
为什么是这样呢?我们分析一下
题目要你求从0行出发的最优值,那么我们就是要找到从第一行出发的最优值,加上第0行到第1行的最优值
但是,很重要的一点,我们需要递归求解,要先求解从倒数第一行出发的最优值,然后根据从倒数第一行出
发的最优值求出从倒数第二行出发的最优值
3.采用自底向上的方式计算问题的最优值:
这个就是我上面说的,要先求解从倒数第一行出发的最优值,然后根据从倒数第一行出发的最优值求出从倒
数第二行出发的最优值,自底向上的计算,迭代的方式求解子问题
4.根据计算最优值时间得到的信息,构造最优解
这个就是问你具体是怎么走的,我们需要在求解子问题的时候保存一些信息,采用构造出最优解(最优值和
最优解是不同的,最优值在本问题中是一个走法中权值之和最大的那一个,而最优解是具体的走法),这里
题目没有要求就是不用去构造最优解,构造起来也挺麻烦的。。。。
解法:
dp【i】【j】:代表从第i行第j列出发得到的最优值
dp【i】【j】=max(dp【i+1】【j】,dp【i+1】【j+1】)+a【i】【j】
表示从第i行第j列出发的最优值等于到i+1行的两种走法中最大的那一个加上出发点的权值
贴个链接:
https://www.cnblogs.com/yinbiao/p/8995253.html
贴个代码:
- #include<bits/stdc++.h>
- using namespace std;
- int main()
- {
- int n;
- scanf("%d",&n);//n行
- int a[n][n];
- memset(a,,sizeof(a));
- for(int i=;i<n;i++)
- {
- for(int j=;j<=i;j++)
- {
- scanf("%d",&a[i][j]);
- }
- }
- int dp[n][n];
- memset(dp,,sizeof(dp));
- for(int j=;j<n;j++)
- {
- dp[n-][j]=a[n-][j];
- }
- for(int i=n-;i>=;i--)
- {
- for(int j=;j<=i;j++)
- {
- dp[i][j]=max(dp[i+][j],dp[i+][j+])+a[i][j];
- }
- }
- printf("%d\n",dp[][]);
- return ;
- }
经典样例2:最长公共子序列问题 (LCS问题)
给你两个序列,问你他们的最长LCS序列的长度是多少?(序列可以是不连续的,只要元素的相对位置一
样)(不了解LCS问题的自行百度)
那么在LCS问题中dp的思想体现在哪里呢?
重复子问题:(超级容易发现的一个)
我们要求x1~xi,Y1~Yj的LCS,那么是不是要求x1~xi-1,Y1~Yi-1的LCS
我们要求x1~xi-1,y1~yi-1的LCS,那么是不是要求x1~xi-2,Y1~yi-2的LCS
所以我们要求的x1~xi,Y1~Yj的LCS这个大问题中,包含了很多的重复子问题
具体做法:
c【i】【j】表示x1~xi,Y1~Yj的LCS序列长度
x【i】==y【j】 c【i】【j】=c【i-1】【j-1】+1
x【i】!=y【j】 c【i】【j】=max(c【i-1】【j】,c【i】【j-1)
i==0||j==0 c【i】【j】=0
贴个代码(求最优值和最优解)
- #include<bits/stdc++.h>
- #define max_v 1005
- using namespace std;
- char x[max_v],y[max_v];
- int dp[max_v][max_v];
- int l1,l2;
- int dfs(int i,int j)
- {
- if(i==-||j==-)
- return ;
- if(x[i]==y[j])//来自左上角
- {
- dfs(i-,j-);
- cout<<x[i]<<" ";//先递归到最后再输出,,这样就是顺序的
- }
- else
- {
- if(dp[i-][j]>dp[i][j-])//来自上面
- {
- dfs(i-,j);
- }
- else//来自左边
- {
- dfs(i,j-);
- }
- }
- return ;
- }
- int main()
- {
- int t;
- scanf("%d",&t);
- getchar();
- while(t--)
- {
- scanf("%s",x);
- scanf("%s",y);
- int l1=strlen(x);
- int l2=strlen(y);
- memset(dp,,sizeof(dp));
- for(int i=; i<=l1; i++)
- {
- for(int j=; j<=l2; j++)
- {
- if(x[i-]==y[j-])
- {
- dp[i][j]=dp[i-][j-]+;
- }
- else
- {
- dp[i][j]=max(dp[i-][j],dp[i][j-]);
- }
- }
- }
- printf("%d\n",dp[l1][l2]);
- dfs(l1,l2);
- cout<<endl;
- }
- return ;
- }
- /*
- 2
- ABCBDAB
- BDCABA
- */
经典样例三:矩阵连乘问题,纸牌问题,石头合并问题(都是一类问题,一起分析)
给定n个矩阵{A1,A2…..An},其中A【i】与A【i+1】是可乘的,如何确定计算的次序,使得乘法的总次数最少
首先我们要明白,计算的次序不同,那么乘法的总次数也不同
类似的问题:给你n张牌,每张排都有一个数字,相邻的两张牌的权值可以相乘,相乘的两张牌可以合并为
一张牌,新牌的权值是原来的两张牌的乘积
这个问题还有石头合并问题都是同一类的问题,属于区间dp问题
石头合并问题:给你一堆石头,排成一行,相邻的两个石头可以合并,合并成的石头的权值为原来两个石头
的权值之和
先来分析矩阵连乘问题:
给你一个一维数组
30,35,15,5,10,20,25
只要相邻的矩阵才可以相乘
思考一下,dp的思想是如何体现的
第一步我们是要把问题分解成很多互相有联系的子问题(重复子问题是用dp的基础)
简单的思考一下,每次矩阵相乘,最简单的就是两个可以相乘的矩阵相乘(A1,A2,A3),那最大的乘法次数就是A1*A2*A3
但是如果是多个呢,我们是不是可以简化成下面这样
A【i】,A【i+1】………………….A【k】………………A【j-1】,A【j】
讲他们分成两个抽象矩阵
第一个:A【i】….A【k】
第二个:A【k+1】…..A【j】
把大问题抽象成两个抽象矩阵相乘,那么更加最简单的那种抽象一下就知道求所有矩阵乘法的最大次数,就
是求第一个抽象矩阵自己内部要乘的次数和第二个抽象矩阵内部自己要求的乘法次数然后加上这这两个抽象
矩阵合并为一个大的抽象矩阵要乘的次数
那么大问题是这样的,大问题里面是不是有很多这样的小问题,而且这些小问题还是重复的,比如A【k】
的选择不同,那么乘的次序结果也不一样,A【k】的选择可以导致很多问题都有重复的部分,如果多次计
算的话,无疑是很不明智的,这样的话跟分治就是没有什么区别了,这样的问题就叫做重复子问题
A【k】的选择不同的话,会导致子问题有很多重复的部分,前面我们说了的,同时A【k】的选择不同的话
会导致两个抽象矩阵相乘的结果也不一样,所以我们就要在所有的A【k】选择中找一个最小的
所以我们现在在这个问题里面找到了dp思想的具体体现:大量的重复子问题
具体做法:
dp【i】【j】:代表矩阵i,矩阵i+1………….矩阵j的最少乘法次数
总结上述:
dp【i】【j】=min(dp【i】【k】+dp【k+1】【j】
i<=k<=j-1
贴个代码:
- #include<bits/stdc++.h>
- using namespace std;
- #define max_v 1005
- int dp[max_v][max_v],a[max_v],s[max_v][max_v];
- void f(int i,int j)
- {
- if(i==j)
- return ;
- f(i,s[i][j]);
- f(s[i][j]+,j);
- printf("A[%d:%d]*A[%d:%d]\n",i,s[i][j],s[i][j]+,j);
- }
- int main()
- {
- int n;
- scanf("%d",&n);
- for(int i=;i<=n;i++)
- {
- scanf("%d",&a[i]);
- }
- for(int i=;i<=n;i++)
- {
- dp[i][i]=;
- }
- for(int r=;r<=n;r++)
- {
- for(int i=;i<=n-r+;i++)
- {
- int j=i+r-;
- dp[i][j]=dp[i+][j]+a[i-]*a[i]*a[j];
- s[i][j]=i;
- for(int k=i+;k<j;k++)
- {
- int t=dp[i][k]+dp[k+][j]+a[i-]*a[k]*a[j];
- if(t<dp[i][j])
- {
- dp[i][j]=t;
- s[i][j]=k;
- }
- }
- }
- }
- f(,n);
- }
- /*
- 6
- 30 35 15 5 10 20 25
- A[2:2]*A[3:3]
- A[1:1]*A[2:3]
- A[4:4]*A[5:5]
- A[4:5]*A[6:6]
- A[1:3]*A[4:6]
- */
分析了矩阵连乘问题,再来分析一下石头合并问题
石头合并问题:其实这个问题跟矩阵连乘问题真的是一样的
非常非常的类似
A1,A2………………….An
也是分解成两个抽象的石头
A【i】,A【i+1】………A【k】……….A【j】
第一个抽象石头:A【i】……..A【k】
第二个抽象石头:A【k+1】…….A【j】
我们现在把大问题分解成了两个抽象的石头合并问题
问的是你合并完成后最小的权值是多少
大问题的最小权值等于第一个抽象石头合并的权值加上第二个抽象石头合并的权值,再加上这两个抽象的石头合并的权值
我们知道,A【k】的选择不同,会导致最后权值的不同,也会导致大量重复的子问题(前面在矩阵连乘wen他中具体分析了)
所以我们要在所有的A【k】选择中,选择一个合并花费最小的
现在我们把大问题分解成了这样一个问题,那么每个抽象的石头也还可以当初一个大问题继续分解呀,所以
就分解成了很多子问题
具体做法:
dp【i】【j】:代表合并第i到第j个石头的最小花费
sum【i】:表示1~i个石头的权值之和
dp【i】【j】=min(dp【i】【k】+dp【k+1】【j】)+sum【j】-sum【i】+a【i】
为什么是sum【j】-sum【i】+a【i】呢?
因为我们要合并从第i个石头到第j个石头所需要的花费就是第i个石头到第j个石头的权值的和呀
贴个代码:
- #include<bits/stdc++.h>
- using namespace std;
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- int a[n+];
- for(int i=; i<=n; i++)
- {
- scanf("%d",&a[i]);
- }
- int sum[n+];
- int dp[n+][n+];
- for(int i=; i<=n; i++)
- {
- int t=;
- for(int j=; j<=i; j++)
- {
- t=t+a[j];
- }
- sum[i]=t;
- }
- for(int i=; i<=n; i++)
- {
- dp[i][i]=;
- }
- for(int r=; r<=n; r++)
- {
- for(int i=; i<=n-r+; i++)
- {
- int j=i+r-;
- int t=dp[i][i]+dp[i+][j]+sum[j]-sum[i]+a[i];
- for(int k=i; k<=j-; k++)
- {
- if(t>dp[i][k]+dp[k+][j]+sum[j]-sum[i]+a[i])
- {
- t=dp[i][k]+dp[k+][j]+sum[j]-sum[i]+a[i];
- }
- }
- dp[i][j]=t;
- }
- }
- printf("%d\n",dp[][n]);
- }
- return ;
- }
- /*
- 样例输入
- 3
- 1 2 3
- 7
- 13 7 8 16 21 4 18
- 样例输出
- 9
- 239
- */
经典样例四:最长递增子序列
比如
1,7,3,5,8,4,8
问你最长的递增的子序列的长度是多少
这个问题的最优解有多个,但是最优值只有一个:长度为4
1,3,5,9
1,3,5,8
1,3,4,8
这三个都是最优解,但是他们长度都是一样的,长度为4
这些是我们看出来的
那我们如何用dp的思想解题呢
第一步分解成很多互相有联系的子问题
要求第n个元素结尾的LIS序列的长度,就要求以第n-1个元素结尾的LIS序列的长度
要求第n-1个元素结尾的LIS序列的长度,就要求以第n-2个元素结尾的LIS序列的长度
…………..
假设第n-1个元素结尾的LIS序列的长度为2,且第n个元素是大于第n-1个元素的(递增的),那么以第n
个元素结尾的LIS序列的长度不就是以第n-1个元素结尾的LIS序列的长度加上1吗?
再回过头来看看这些子问题
他们中是不是含有大量重复的子问题
dp【n】:代表以第n个元素结尾的LIS序列的长度
比如我要求dp【n】,就要求dp【n-2】,dp【n-3】
在要求dp【n-1】的时候,也还要求dp【n-2】,dp【n-3】一次
这个就是求了很多次,想想当n足够大的时候,子问题足够多的时候,求的重复的子问题是不是很多很多
这样的话速度太慢
所以这个时候,dp的作用就是体现出来了,保存已经求解过的子问题的值,下次又遇到这个子问题的时
候,直接拿出来用就好啦
做法:
dp【1】=1
dp【i】=max(dp【j】+1) 要求:a【j】<a【i】,j<i
就是在第i个元素的前面找到LIS序列长度最大的,加上1,(先决条件是递增的)
贴个代码:(最优值和一个最优解)
- #include<bits/stdc++.h>
- using namespace std;
- #define max_v 1005
- int a[max_v],dp[max_v];
- void f(int n,int result)
- {
- bool flag=false;
- if(n<||result==)
- return ;
- if(dp[n]==result)
- {
- flag=true;
- result--;
- }
- f(n-,result);
- if(flag)
- printf("%d ",a[n]);
- }
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- for(int i=;i<n;i++)
- {
- scanf("%d",&a[i]);
- }
- memset(dp,,sizeof(dp));
- dp[]=;
- for(int i=;i<n;i++)
- {
- int t=;
- for(int j=;j<i;j++)
- {
- if(a[j]<a[i])
- {
- if(t<dp[j])
- {
- t=dp[j];
- }
- }
- }
- dp[i]=t+;
- }
- int t=;
- for(int i=;i<n;i++)
- {
- if(t<dp[i])
- {
- t=dp[i];
- }
- }
- printf("%d\n",t);
- f(n,t);
- printf("\n");
- }
- return ;
- }
- /*
- 输入:
- 7
- 1 7 3 5 9 4 8
- 输出:
- 4
- 1 3 4 8*/
经典样例五:最大子段和问题
比如:
-2,11,-4,13,-5,-2
什么叫最大字段和?就是连续的数字的和最大是多少,注意是段,而不是序列,序列可以是离散的,而段必
须的连续的
所以这个问题dp思想体现在哪里呢?
这个问题其实跟LIS,LCS问题都差不多,都是线性dp问题
第一步:分解成很多有联系的子问题
要求以第n个元素结尾的最大字段和是多少,就要求以第n-1个元素结尾的最大字段和是多少
要求以第n-1个元素结尾的最大子段和是多少,就要求以第n-2个元素结尾的最大字段和是多少
为什么是这样呢?
仔细思考一下
以求第n-1个元素的1最大字段和为例
如果我们知道了以第n-2个元素的最大字段和是多少,如果是正的,加上第n个元素值即可,如果是负数,
那还不如不加呢,这样第n个元素的最大字段和还大一点,因为你加上一个负数肯定比原来的数小了呀
那么dp思想中的重复子问题体现在哪里呢?
体现在第一步,跟LIS问题中的体现是一样的,这里不再赘述
贴个代码:(最优解)
- #include<bits/stdc++.h>
- using namespace std;
- int main()
- {
- int t;
- scanf("%d",&t);
- while(t--)
- {
- int n;
- scanf("%d",&n);
- int a[n+];
- for(int i=;i<=n;i++)
- {
- scanf("%d",&a[i]);
- }
- int dp[n+];
- dp[]=a[];
- for(int i=;i<=n;i++)
- {
- int x=dp[i-];
- if(x<)
- {
- x=;
- }
- dp[i]=x+a[i];
- }
- int maxvalue=dp[];
- for(int i=;i<=n;i++)
- {
- if(maxvalue<dp[i])
- {
- maxvalue=dp[i];
- }
- }
- printf("%d\n",maxvalue);
- }
- return ;
- }
- /*
- 输入:
- 2
- 6
- -2 11 -4 13 -5 -2
- 输出;
- 20
- */
经典样例六:01背包问题
背包问题可以说是很经典的问题之一了,01背包问题,就是说每个物品只有两种选择,装还不装,且物品
不可分割
我先不讲01背包问题应该怎么做,讲01背包里面蕴含的dp思想
dp适用于多阶段决策问题,就是每个阶段都要做决策,且你做的决策会影响的最终的结果,导致最终结果的值有所不同
这个决策的概念在01背包里面用的可以说是体现的非常非常的透彻了,因为你每个阶段都要做决策呀,这
个物品我到底是选还是不选呢
声明一个 大小为 m[n][c] 的二维数组,m[ i ][ j ] 表示 在面对第 i 件物品,且背包容量为 j 时所能获得的最大价值 ,那么我们可以很容易分析得出 m[i][j] 的计算方法,
(1). j < w[i] 的情况,这时候背包容量不足以放下第 i 件物品,只能选择不拿
m[ i ][ j ] = m[ i-1 ][ j ]
(2). j>=w[i] 的情况,这时背包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大的价值。
如果拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,背包容量为j-w[i]时的最大价值,也是相当于为第i件物品腾出了w[i]的空间。
如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)
究竟是拿还是不拿,自然是比较这两种情况那种价值最大。
状态转移方程:
if(j>=w[i])
m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
else
m[i][j]=m[i-1][j];
代码如下:(二维dp解决01背包问题,一维dp解决01背包问题)
- #include<bits/stdc++.h>
- using namespace std;int ZeroOnePack(int v[],int w[],int n,int c)//v1,v2....vn价值 w1,w2,w3...wn重量 n表示n个物品 c表示背包容量
- {
- int dp[n+][c+];
- memset(dp,,sizeof(dp));
- for(int i=; i<=n; i++)
- {
- for(int j=; j<=c; j++)
- {
- if(j>=w[i])
- {
- dp[i][j]=max(dp[i-][j],dp[i-][j-w[i]]+v[i]);//第i个物品放入之后,那么前面i-1个物品可能会因为剩余空间不够无法放入
- }
- else
- {
- dp[i][j]=dp[i-][j];
- }
- }
- }
- return dp[n][c];
- }
- //空间优化,采用一维数组
- int ZeroOnePack_improve(int v[],int w[],int n,int c)//v1,v2....vn价值 w1,w2,w3...wn重量 n表示n个物品 c表示背包容量
- {
- int dp[c+];
- memset(dp,,sizeof(dp));
- for(int i=; i<=n; i++)
- {
- for(int j=c; j>=; j--)
- {
- if(j>=w[i])
- dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
- }
- }
- return dp[c];
- }
- int main()
- {
- int t;
- scanf("%d",&t);
- while(t--)
- {
- int n,c;
- scanf("%d %d",&n,&c);
- int v[n+],w[n+];
- for(int i=; i<=n; i++)
- {
- scanf("%d",&v[i]);
- }
- for(int i=; i<=n; i++)
- {
- scanf("%d",&w[i]);
- }
- // printf("%d\n",ZeroOnePack(v,w,n,c));
- printf("%d\n",ZeroOnePack_improve(v,w,n,c));
- }
- return ;
- }
- /*
- 1
- 5 10
- 1 2 3 4 5
- 5 4 3 2 1
- 14
- */
到底什么是dp思想(内含大量经典例题,附带详细解析)的更多相关文章
- 经典剪枝算法的例题——Sticks详细注释版
这题听说是道十分经典的剪枝算的题目,不要问我剪枝是什么,我也不知道,反正我只知道用到了深度搜索 我参考了好多资料才悟懂,然后我发现网上的那些大神原理讲的很明白,但代码没多少注释,看的很懵X,于是我抄起 ...
- hdu 3030 Increasing Speed Limits (离散化+树状数组+DP思想)
Increasing Speed Limits Time Limit: 2000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java ...
- DP思想在斐波那契数列递归求解中的应用
斐波那契数列:1, 1, 2, 3, 5, 8, 13,...,即 f(n) = f(n-1) + f(n-2). 求第n个数的值. 方法一:迭代 public static int iterativ ...
- c语言面试宝典(经典,超详细)
c语言面试宝典(经典,超详细) 2018年08月25日 09:32:19 chengxuyuan997 阅读数:7799 摘自:https://blog.csdn.net/chengxuyuan9 ...
- 状压dp学习笔记(紫例题集)
P3451旅游景点 Tourist Attractions 这个代码其实不算是正规题解的(因为我蒟蒻)是在我们的hzoj上内存限制324MIB情况下过掉的,而且经过研究感觉不太能用滚动数组,所以那这个 ...
- C语言经典例题100
C语言经典例题100 来源 http://www.fishc.com 适合初学者 ----------------------------------------------------------- ...
- C语言中的经典例题用javascript怎么解?(一)
C语言中的经典例题用javascript怎么解?(一) 一.1+2+3+……+100=? <script type="text/javascript"> ...
- 【JS中循环嵌套常见的六大经典例题+六大图形题,你知道哪几个?】
首先,了解一下循环嵌套的特点:外层循环转一次,内层循环转一圈. 在上一篇随笔中详细介绍了JS中的分支结构和循环结构,我们来简单的回顾一下For循环结构: 1.for循环有三个表达式,分别为: ①定义循 ...
- 整数划分——真正的递归经典例题(NYOJ——90)
先注明学习博客的地址:(http://www.cnblogs.com/hoodlum1980/archive/2008/10/11/1308493.html) 题目描述:任何正整数n都可以写成n=n1 ...
随机推荐
- Redis-跳跃表
相当于Redis 中的 sorted set 跳跃表节点结构: typedef struct zskiplistNode { struct zskiplistNode *backward; //后退 ...
- SuperSubScriptHelper——Unicode上下标辅助类
在项目的实施过程中,类似化学分子式.平方.立方等,需要处理上.下标字符. 上下标字符的实现,大致有两种方式,一种是字符本身包含上下标信息,另一种方式是通过格式化标记实现上下标字符的显示. Word中的 ...
- Oracle数据库基本操作(一) —— Oracle数据库体系结构介绍、DDL、DCL、DML
一.Oracle数据库介绍 1.基本介绍 Oracle数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/ ...
- 【Chromium】GPU进程启动流程
本篇文档以gpu进程的创建和启动为例,讲述chormium如何启动一个browser进程的子进程 PS:本文使用的chromium代码版本为71 前言 GPU进程的启动时机是由browser进程负责的 ...
- sql 模糊搜素拼接
if($irb_order!=''){ $condition .= " AND d.irb_order like '%".$irb_order."%'"; } ...
- JavaScript & jQuery & Bootstrap
一.前言 javascript 简称 JS 与java编程语言 没有什么关系 JavaScript: {核心(ECMAScript) 文档对象模型(DOM) Document object mode ...
- Luogu4191:[CTSC2010]性能优化
传送门 题目翻译:给定两个 \(n\) 次多项式 \(A,B\) 和一个整数 \(C\),求 \(A\times B^C\) 在模 \(x^n\) 意义下的卷积 显然就是个循环卷积,所以只要代入 \( ...
- 51nod1538:一道难题(常系数线性递推/Cayley-Hamilton定理)
传送门 Sol 考虑要求的东西的组合意义,问题转化为: 有 \(n\) 种小球,每种的大小为 \(a_i\),求选出大小总和为 \(m\) 的小球排成一排的排列数 有递推 \(f_i=\sum_{j= ...
- 1142 奖学金 sort做法
个人博客:doubleq.win 1142 奖学金 2007年NOIP全国联赛普及组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题解 题目 ...
- Intelij IDEA 配置Tomcat时找不到 “Application Server”
由于公司突然断电,再打开idea的时候,tomcat就消失了.然后在网上搜了一下,没搜到自己乱点了一下. 如图 : plugins >> application servers Vie ...