算法作业-动态规划

第一题


目标函数为 max g1(x1)+g2(x2)+g3(x3),其中x1、x2、x3的约束为它们均为非负整数,且它们平方的和小于等于10,所以该问题可以看作0-1背包问题,背包的最大容量是10,求目标函数的max值即求背包的最大价值。
设两个数组,F[k][y]和g[i][j],其中F[k][y]表示在平方的和的限制为y且取前k个x时的最大值,g[i][j]表示函数gi(xi)的值,从表中可以看出g(x)函数值与选择xk以及xk的值有关。
函数的递推式为:

F[k][y] = max F[k-1][y-xk*xk] + g[k,xk]

当平方的和约束为y且选择前k个x时,其最大值为在平方和约束为 y - xk * xk 且选择前k-1个x时的最大值再加上gk(xk)。选择前k个x,那么选择的xk的值也在平方的和y的限制中,它的前一个状态就是选择k-1个x,且平方的和限制为 y - xk * xk,也即减去了第k个x,前一个状态的最大值加上第k个x自己的值,选择二者之和的最大值作为现在状态的值,得到结果。

for(int k=1; k<=3; k++)
{
    for(int y=0; y<=10; y++)
    {
        int max=0;
        //x的平方不能大于y,否则下边y-pow(x,2)会越界
        for(int x=0; x<=sqrt(y); x++)
        {           
            if(F[k-1][y-pow(x,2)]+g[k,x]>max)
            {
                max=F[k-1][y-pow(x,2)]+g[k,x];
                //标记限制为y且选择前k个x时第k个x的值
                Flag[k,y]=x;
            }
        }
    }
}

//输出
//最优值
cout<<F[3][10]<<endl;
int i=3,y=10;
while(i)
{
    cout<<"x"<<i<<"="<<Flag[i][y]<<endl;
    y-=Flag[i][y]*Flag[i][y];
    i--
}
/*
结果
x1=1,x2=2,x3=2,最大值37
*/

最后输出就是最终条件下选择的第三个x的值,然后y减去这个x的平方,i-1,F[i-1][y-pow(x,2)]就是选择的第二个x的值,第一个x的值也是如此。

第二题


首先,这个题是给定了n个整数组成的序列且分成m段,已经指出了要分成多少段。
用动态规划求解,声明dp[i][j]数组,表示i个整数分成j段的子序列和的最大值的最小值,所谓子序列和的最大值的最小值,因为将i个数分成j段,有很多种分法,对于每一种分法,这j个子序列和都有一个最大值,所以每一种分法都有一个最大值,最优的分段法就是比较每一种分法的子序列和的最大值,其中的最小值的那种分法就是最优分段法
对于dp[i][j]数组,首先要进行初始化

for(int i=1; i<=n; i++)
{
    dp[i][1]=dp[i-1][1]+a[i];
}

这里数组a[i]保存了输入的整数,dp[i][1]就是把i个数分成一份,也即这i个数的和,因此 dp[i][1]就等于dp[i-1][1]再加上a[i],也即前i-1个数的和加上第i个数。

递推公式:

tmp = max dp[k][j-1], dp[i][1]-dp[k][1]
dp[i][j] = min tmp

递推公式分为两部分,一是选取子序列和的最大值,二是选择这些最大值中的最小值。
选取子序列和的最大值时比较了最后一段子序列和前面j-1段子序列和的最大值,这里k为前面j-1段子序列的最后一位的下标,dp[i][j]表示i个数分成j段,这里把最后一段和前面分开,dp[k][j-1]就是前面k个数分成j-1段的情况下子序列和最大值的最小值,dp[i][1]-dp[k][1]就是整个序列的和减去前k个数的和,就等于最后一段子序列的和,选择二者之中的最大值就得到了分成j段时子序列和的最大值,最后dp[i][j]等于最大值中的最小值。

for(int i=1; i<=n; i++)
{
    //n个数,除去分一段,最少分2段,最多分n段
    //这里有限制,最多只能是m段
    for(j=2; j<=m; j++)
    {
        //INF=0x3f3f3f3f
        min=INF;
        //一共是i个数
        for(int k=1; k<=i; k++)
        {
            tmp=max(dp[k][j-1],dp[i][1]-dp[k][1]);
            if(tmp<min)
            {
                min=tmp;
            }
        }
        dp[i][j]=min;
    }
}
//输出
cout<<dp[n][m]<<endl;
posted @ 2022-04-28 00:04  HD0117  阅读(197)  评论(2编辑  收藏  举报