最大字段和:
转移方程 : f[i] = max(a[i], f[i-1] + a[i])
对于要求字段起止位置的
for(int i=1;i<=T;i++){
scanf("%d", &n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int start = 1, ti = 1;
int ed = 1, ans = a[1];
f[1] = a[1];
for(int i=2;i<=n;i++){
if(f[i-1] >= 0){
f[i] = f[i-1] + a[i];
}else{
f[i] = a[i];
ti = i;
}
if(f[i]>ans){
ans = f[i];
start = ti;
ed = i;
}
}
LIS模型
暴力动态规划
只采用最朴素的动态规划,复杂度 O ( N 2 ) O(N^2) O(N2)
for(int i=1;i<=n;i++){
dp[i] = 1;
for(int j = i-1;j;j--){
if(f[j] < f[i])dp[i] = max(dp[i], dp[j] + 1);
}
}
线段树\树状数组优化
通过线段树或者树状数组维护[1, a[i]]
中len
的最大值,可将查询优化至 l o g n log^n logn,所以总复杂度为 O ( N l o g N ) O(Nlog^N) O(NlogN),
#include
#include
#include
#include
using namespace std;
int tr[50010];
int a[100010];
int n, big;
int lowbit(int x) {
return x & -x;
}
void add(int x, int k) {
for (int i = x; i <= big; i += lowbit(i))tr[i] = max(tr[i], k);
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i))res = max(res, tr[i]);
return res;
}
int main()
{
while (scanf("%d", &n) != EOF) {
big = 0;
for (int i = 1; i <= n; i++)scanf("%d", &a[i]),big = max(big,a[i]);
int len = 0;
for (int i = 1; i <= n; i++) {
int x = query(a[i]) + 1;
len = max(len, x);
add(a[i] + 1, x);
}
printf("%d\n", len);
memset(tr, 0, sizeof tr);
}
}
二分优化
c++中二分的库函数:
lower_bound
会找出序列中第一个大于等于 x x x的数
upper_bound
会找出序列中第一个大于 x x x的数
用法:lower_bound(start position, end position, x)
,函数返回位置指针
算法: 定义a[i]
为原数组的第 i i i位,f[i]
为最长上升子序列的第 i i i位,len
为最长上升子序列的长度
f[len] > a[i]
则找到f
中大于a[i]
的最小的数然后替换,否则把f[++len] = a[i]
len
即为答案代码:
for (int i = 2; i <= n; i++) {
if (f[len] > a[i]) {
int* p = upper_bound(f + 1, f + 1 + len, a[i]);
*p = a[i];
}
else {
f[++len] = a[i];
}
}
走格子模型: (属于基础性DP,结合题意注意边界条件即可)
加强对边界条件的敏感度
LCS及其变形:
普通LCS的转移方程为f[i][j] = max{f[i-1][j-1] + 1,f[i-1][j],f[i][j-1]};
直接写即可
而附加条件的LCS(一般为配对附加权值),如HDU-1080_Human Gene Functions
与一般的类似,但要注意边界条件以及权值的处理
//'HUD_1080'
#include
#include
#include
#include
#include
using namespace std;
int p[105][105];
void found()
{
p['A']['A']=p['C']['C']=p['G']['G']=p['T']['T']=5;
p['A']['C']=p['C']['A']=p['A']['T']=p['T']['A']=-1;
p[' ']['T']=p['T'][' ']=-1;
p['A']['G']=p['G']['A']=p['C']['T']=p['T']['C']=-2;
p['G']['T']=p['T']['G']=p['G'][' ']=p[' ']['G']=-2;
p['A'][' ']=p[' ']['A']=p['C']['G']=p['G']['C']=-3;
p['C'][' ']=p[' ']['C']=-4;
}
int main()
{
found();
int t,d[105][105],i,j,l1,l2;;
char s1[105],s2[105];
scanf("%d",&t);
while(t--)
{
scanf("%d %s",&l1,s1+1);
scanf("%d %s",&l2,s2+1);
d[0][0]=0;
for(i=1;i<=l1;i++)
{
d[i][0]=d[i-1][0]+p[s1[i]][' '];
}
for(i=1;i<=l2;i++)
{
d[0][i]=d[0][i-1]+p[' '][s2[i]];
}
for(i=1;i<=l1;i++)
{
for(j=1;j<=l2;j++)
{
d[i][j]=max(d[i-1][j]+p[s1[i]][' '],d[i][j-1]+p[' '][s2[j]]);
d[i][j]=max(d[i][j],d[i-1][j-1]+p[s1[i]][s2[j]]);
}
}
printf("%d\n",d[l1][l2]);
}
}
简单DP,略
完全背包问题的优化:
定义 f [ i ] [ j ] f[i][j] f[i][j]为选到第 i i i组物品,且容量为 j j j的选法的最大值
所以有 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − v i ] + w i , f [ i − 1 ] [ j − 2 × v i ] + 2 × w i , … , f [ i − 1 ] [ j − k × v i ] + k × w i } f[i][j] = max\{ f[i-1][j],f[i-1][j - v_i] + w_i,f[i-1][j - 2\times v_i] + 2\times w_i,\dots,f[i-1][j - k\times v_i] + k \times w_i \} f[i][j]=max{f[i−1][j],f[i−1][j−vi]+wi,f[i−1][j−2×vi]+2×wi,…,f[i−1][j−k×vi]+k×wi}
又有 f [ i ] [ j − w i ] = m a x { f [ i − 1 ] [ j − v i ] , f [ i − 1 ] [ j − 2 × v i ] + w i , f [ i − 1 ] [ j − 3 × v i ] + 2 × w i , … , f [ i − 1 ] [ j − k × v i ] + k × w i } f[i][j - w_i] = max\{ f[i-1][j-v_i],f[i-1][j - 2 \times v_i] + w_i,f[i-1][j - 3\times v_i] + 2\times w_i,\dots,f[i-1][j - k\times v_i] + k \times w_i \} f[i][j−wi]=max{f[i−1][j−vi],f[i−1][j−2×vi]+wi,f[i−1][j−3×vi]+2×wi,…,f[i−1][j−k×vi]+k×wi}
因为容量不变,所以 k k k也不会发生改变
所以 f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] , f [ i ] [ j − w i ] } f[i][j] = max\{ f[i-1][j],f[i][j-w_i] \} f[i][j]=max{f[i−1][j],f[i][j−wi]}
所以可以优化掉一层循环
再加滚动数组可再优化一维数组
二维费用背包与其他背包的结合:
待更新
待更新
待更新
待更新