石子合并问题——直线

简单的区间dp,dp[i][j]表示从i~j区间所得的最大最小分。
以最大得分为例:
1.首先初始化dp函数,为0;
2.计算出前缀和;
3.枚举区间长度len;
4.枚举起始区间的位置j;
5.因为将新的一堆石子的数量记为得分数,以dp[1][2]为例,
第一堆第二堆石子合并后数量为a[1]+a[2],即sum[2]-sum[0],
这是得分数,dp[1][2] = dp[1][1]+dp[2][2]+sum[2]-sum[0];
所以dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
含义是第i~k堆的石子加上k+1~j堆的石子,在合并k和k+1堆时
分数是第i~j堆石子数量的总和

石子合并问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int main()
{
    int dp[101][101],sum[101],a[101],n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d",&a[i]);
            sum[i] = sum[i-1]+a[i];
        }
        for(int len = 2 ; len <= n ; len++)//枚举区间长度 
        {
            for(int i = 1 ; i <=n ;i++)
            {
                int j = i+len-1;
                if(j>n) continue;
                for(int k = i ; k < j ;k++)
                {
                    dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                }
            }
        }
        int a1 = dp[1][n];

        for(int i = 0 ; i <= n ; i++)
        for(int j = 0 ; j <= n ; j++)
        dp[i][j] = 1e8;
        for(int i = 0 ;i<=n ;i++)
        dp[i][i] = 0;
        for(int len = 2 ; len <= n ; len++)
        {
            for(int i = 1 ; i <=n ;i++)
            {
                int j = i+len-1;
                if(j>n) continue;
                for(int k = i ; k < j ;k++)
                {
                    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                }
            }
        }
        int a2 = dp[1][n];
        cout<<a2<<" "<<a1<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40240576/article/details/81545018
今日推荐