51nod 1021 石子归并

版权声明:个人笔记,仅供复习 https://blog.csdn.net/weixin_42373330/article/details/88932170

题目描述:

动态规划
1021 石子归并
1 秒 131,072 KB 20 分 3 级题
N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
输入
第1行:N(2 <= N <= 100)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
输出
输出最小合并代价
输入样例
4
1
2
3
4
输出样例
19


分析:dp没思路,公式没写出来,不知道咋么做,下面用递归做的,写得有点头疼,过了部分样例

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define ll long long
//51nod -1021
const int N= 1e4+10;
int a[N];
ll f(int a[],int n,ll &value,ll &mx)//返回的是该段的总和,value表示该段的花费,mx是用来取最小的value的
{
    if(n==2)
    {
        value =a[0]+a[1];
        mx=value;
        return a[0]+a[1];
    }
    else if(n==1)
    {
        value =0;
        mx=0;
        return a[0];
    }
    ll sum;
    for(int i=1;i<=n-1;i++)//把该段分成两部分,i的位置表示分界的地方
    {
        int b[N],c[N];
        for(int j=0;j<i;j++)
            b[j]=a[j];
        for(int j=i;j<n;j++)
            c[j-i]=a[j];
        ll value1,value2;
        ll mx1,mx2;
        sum= f(b,i,value1,mx1)+f(c,n-i,value2,mx2);
        value=value1+value2+sum;//这段的value等于两端的value之和,加上最后的两端
        if(i==1)
            mx=sum+value1+value2;
        else
            mx=min(mx,value);

    }
    return sum;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    ll value,mx;
    f(a,n,value,mx);
    cout<<mx<<endl;
    return 0;
}


dp方法
公式:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
dp[i][j]表示第i堆到第j堆(含i,j)合并成一堆所需的最小代价

#include <iostream>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define ll long long
const int mod= 1000000007;
const int N=1e6+10;
int n;
int a[110];
int sum[110];
int dp[110][110];
void f()
{
   sum[0]=0;
   for(int i=1;i<=n;i++)
       sum[i]=sum[i-1]+a[i];
}
int main()
{
   memset(dp,0,sizeof(dp));
   cin>>n;
   for(int i=1;i<=n;i++)
       cin>>a[i];
   f();
   int len;//表示长度,由2到n遍历
   for(len=2;len<=n;len++)
   {
       for(int i=1;i<n;i++)
       {
           int j=i+len-1;
           for(int k=i;k<j&&j<=n;k++)
           {
               if(dp[i][j]==0)
                   dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
               else
                   dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
           }

       }
   }

   cout<<dp[1][n]<<endl;
   return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42373330/article/details/88932170