动态规划法

  动态规划算法和分治法类似,其基本思想也是将待求解问题分解成若干子问题,但是经分解得到的子问题的数目只是多项式量级,而且子问题不是互相独立的。可以避免分治法中子问题被重复计算很多次。

两个要素:(1)子问题重叠性质(2)最优子结构性质

子问题重叠性质:(递归算法求解时)有些子问题被反复计算多次。动态规划法中对每一个子问题只解一次。

最优子结构性质:当一个问题的最优解包含了其子问题的最优解。

全源最短路径

Floyed算法采用自底向上方式计算,dist[i][j]存放从结点i到j的最短路径的长度,在算法的第k步上,做出决策:从i到j的最短路径上是否包含结点k,时间复杂度O(n^3)

最长公共子序列

dp[i][j] 表示第一个序列的前i个字符和第二个序列的前j个字符的最长公共子序列的长度,动态转移方程为:

dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0)),表示在这三种状态中取到最大值,

(1)第一种状态表示不录入第一个序列的第i个字符时的最长公共子序列;
(2)第二种状态表示不录入第二个序列的第j个字符时的最长公共子序列;
(3)第三种状态表示第一个序列的前i-1个字符与第二个序列前j-1个字符的公共子序列加上最后一个字符的录入状态,如果最后的一个字符相等则录入1,否则为0。
然后根据动归的状态,来判断我们要求得的序列中的字符有哪些。
#define INF 0x3f3f3f3f
char a[1001], b[1001];
int dp[1001][1001], len1, len2;
void lcs(int i, int j)
{
    for(i=1; i<=len1; i++)
    {
        for(j=1; j<=len2; j++)
        {
            if(a[i-1] == b[j-1])///a[i]和b[j]相等
                dp[i][j] = dp[i-1][j-1] + 1;
            else if(dp[i-1][j] > dp[i][j-1])
                dp[i][j] = dp[i-1][j];
            else
                dp[i][j] = dp[i][j-1];
        }
    }
} 

void llcs()///根据dp求得最长公共子序列
{
    int i, j, z = 0;
    char c[1001];
    memset(c, 0, sizeof(c));
    i = len1, j = len2;
    while(i!=0 && j!=0)
    {
        if(a[i-1] == b[j-1])
        {
            c[z++] = a[--i];
            j--;
        }
        else if(dp[i-1][j] < dp[i][j-1])
            j--;
        else if(dp[i][j-1] <= dp[i-1][j])
            i--;
    }
    for(i=z-1; i>=0; i--)
        printf("%c", c[i]);
    printf("\n");
}

int main()
{
    while(scanf(" %s", a)!=EOF)
    {
        scanf(" %s", b);
        memset(dp, 0, sizeof(dp));
        len1 = strlen(a);
        len2 = strlen(b);
        lcs(len1, len2);
        llcs();
    }
    return 0;
}
最长公共子序列

0/1背包问题

0/1背包问题可以看作是决策一个序列(x1, x2, …, xn),对任一变量xi的决策是决定xi=1还是xi=0。在对xi-1决策后,已确定了(x1, …, xi-1),在决策xi时,问题处于下列两种状态之一:

(1)背包容量不足以装入物品i,则xi=0,背包不增加价值;

(2)背包容量可以装入物品i,则xi=1,背包的价值增加了vi。

int v[maxn],w[maxn],dp[maxn];///dp[m]是背包容量为m时能装入的最大价值
int main()
{
    int t,i,j,n,m;
    cin>>t;
    while(t--)
    {
       cin>>n>>m;///n是物品数量,m是背包容量
       for(i=1;i<=n;i++)
         cin>>v[i];///价值
       for(i=1;i<=n;i++)
         cin>>w[i];///体积
       memset(dp,0,sizeof(dp));
       for(i=1;i<=n;i++)
       {
          for(j=m;j>=w[i];j--)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
        cout<<dp[m]<<endl;
    }
}
0/1背包

完全背包

有n种物品和一个容量为v的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

int dp[50010];
int w[2010],v[2010];///w是物品体积,v是物品价值
int main
{
  int i,j,t,n,m,a,b;
  scanf("%d",&t);
  while(t--)
  {
     scanf("%d%d",&n,&m);///n是物品数量,m是背包容量
     for(i=1;i<=n;i++)
       scanf("%d"%d",&w[i],&v[i]);
     memset(dp,-inf,sizeof(dp));
     dp[0]=0;
     for(i=1;i<=n;i++)
     {
        for(j=w[i];j<=m;j++)
          dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
     }
     if(dp[m]>0) printf("%d\n",dp[m]);
     else printf("No\n");
  }  
  return 0;
}
完全背包

 与01背包相比,完全背包只是第二重循环的顺序发生了改变。保证用较小容量的最大价值去更新较大容量的最大价值,不重不漏。

posted @ 2020-07-25 17:44  Littlejiajia  阅读(225)  评论(0编辑  收藏  举报