【动态规划】【子序列模板】

来源:互联网 发布:淘宝人生全文免费阅读 编辑:程序博客网 时间:2024/05/17 23:56

ACM模板

【最长上升子序列】

输出长度

O(n*n)

while(scanf("%s%s",s1+1,s2+1)!=EOF)    {        l1 = strlen(s1+1);        l2 = strlen(s2+1);        for(i = 0; i <= l1;i ++)            f[i][0] = 0;        for(i = 0; i <= l2; i ++)            f[0][i] = 0;        for(i = 1; i <= l1; i ++)        {            for(j = 1; j <= l2; j ++)            {                if(s1[i] == s2[j])//字符相等时,上一个状态长度加1                     f[i][j] = f[i-1][j-1] + 1;                else                {//字符不等时,取最大的字符序列公共长度                     if(f[i-1][j] > f[i][j-1])                        f[i][j] = f[i-1][j];                    else                        f[i][j] = f[i][j-1];                }            }        }        printf("%d\n",f[l1][l2]);    }

O(nlogn)

int find(int flag,int *end,int low,int high){//二分法查找end中最小的小于数flag的下标     int mid;    while(low < high)    {        mid = (low+high)>>1;        if(flag > end[mid])            low = mid + 1;        else            high = mid;    }    return low;}int main(){//end数组存储的是子序列长度为i的最小尾数     end[1] = num[1],len = 1;//len记录子序列最长长度     for(i = 2; i <= n; i ++)    {        if(num[i] > end[len])//如果求非递减的序列则加上等号             ans = ++len;        else            ans = find(num[i],end,1,len+1);        end[ans] = num[i];    }    printf("%d\n",len);}

回溯输出最长上升子序列 O(n*n)

void LCS(char *x,char *y,int n,int m,int a[][N+10],int b[][N+10] ){//找到最长公共子序列    for(i = 0; i <= n; i ++)        a[i][0] = 0;    for(j = 0; j <= m; j ++)        a[0][j] = 0;    for( i = 1; i <= n; i ++)        for( j = 1; j <= m; j ++)        {            if(x[i-1] == y[j-1])            {//下标从0开始,故为i-1,j-1。                a[i][j] = a[i-1][j-1]+1;                b[i][j] = 1; //字符相等标记为1            }            else             {                if( a[i-1][j] > a[i][j-1])                {                    a[i][j] = a[i-1][j] ;                    b[i][j] = 0;//字符串1大标记为0                }                else                {                    a[i][j] = a[i][j-1] ;                    b[i][j] = -1;//字符串2大标记为1                }            }        }    return;}void PrintLcs(char *s,int n,int m,int b[][N+10]){//回溯输出子序列    if( 0 == n|| 0 == m)        return;    else if( b[n][m] == 1)    {        PrintLcs(s,n-1,m-1,b);        printf("%c",s[n-1]);    }    else if( b[n][m] == 0)        PrintLcs(s,n-1,m,b);    else if(b[n][m] == -1)        PrintLcs(s,n,m-1,b);}int main(){    LCS(str1,str2,l1,l2,a,b);    PrintLcs(str1,l1,l2,b);    printf("\n");    return 0;}

【最大上升子段和】

/*max_len[N]存1~n各阶段的最大上升子段和*/        max_len[1] = num[1];//数组开始的最大上升子段和是第一个元素          max = num[1];//最大上升子段和初始化          for( i = 1; i <= n; i ++)          {              num_len = 0;//记录i左边的最大上升子段和              for(j = 1; j < i; j ++)//找到i左边的最大上升子段和              {                  if(num[i] > num[j])                  {                      if(num_len < max_len[j])                          num_len = max_len[j];                  }//if             } //for             max_len[i] = num_len + num[i]; //i左边最大上升子段和加上自身的值             if(max_len[i] > max)                  max = max_len[i];          }//for          printf("%d\n",max); //输出最大和 

【连续最大子段和之输出最大子段和,起点,终点】

时间复杂度为O(n),舍弃数组存储的方法原因有:1:易粗心错估数据范围,2:降低时间复杂度

#include<stdio.h>  int max_start,max_end;//最终的起始点和最终的终点 int now_start;  //当前起始点 int now_sum;  //当前的和 int max;//最大子段和 int num; //读入当前值 int main()  {      int n,i;    while(scanf("%d",&n)!=EOF)      {            for(i = 1; i <= n; i ++)          {              scanf("%d",&num);              if( i == 1) //如果是第一个值,初始化数据             {                  now_sum = max = num;                  now_start = max_end = i;              }              else              {                  if( now_sum >= 0)                      now_sum += num;                  else                  {                      now_sum = num;                      now_start = i;  //记录当前起始点                 }              }              if( now_sum >= max) //更新最大值             {                  max = now_sum;                  max_start = now_start; //更新起始点                 max_end = i;  //更新终止点             }          }          printf("%d %d %d\n",max,max_start,max_end);  //输出最大子段和,起始点,终止点     }      return 0;  }