整数划分——区间dp(石子合并)

这不是将一个数以一来划分,而是把一个整数以位来划分

题目描述

如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大。N、M从键盘输入,输出最大值及一种划分方式。

输入格式

第一行一个正整数T(T<=10000),表示有T组数据。

接下来T行每行两个正整数N,M。

输出格式

对于每组数据

第一行输出最大值。

第二行输出划分方案,将N按顺序分成M个数输出,两个数之间用空格格开。

样例

样例输入

1
199 2

样例输出

171
19 9

这是递归思想,动态规划是正向的,而判断后是逆向的,输出时运用回溯,达到正向输出的目的
以下是代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
unsigned long long t,n[21],n2,n3[21][21],x,son[1000][1000],f[21][21],m;//数据极大,用无符号长整型
string n1;
int printf1(int a,int b)//输出函数,回溯
{
    if(b==0)return 0;
    printf1(son[a][b],b-1);
    for(int i=son[a][b]+1;i<=a;i++)
        cout<<n[i];
    cout<<" ";
}
int main()
{
    cin>>t;
    for(int l=1;l<=t;l++)
    {
        memset(n,0,sizeof(n));
        memset(son,0,sizeof(son));
        cin>>n1>>m;
        n2=n1.length();
        for(int i=0;i<=n2;i++)
            for(int j=0;j<=n2;j++)
            {
                f[i][j]=0;
                //n3[i][j]=1;
            }    
        f[0][0]=1;
        for(int i=1;i<=n2;i++)
        {
            n[i]=n1[i-1]-'0';
            //cout<<n[i];
        }
        for(int i=1;i<=n2;i++)
        {
            x=n[i];
            for(int j=i;j<=n2;j++)
            {
                n3[i][j]=x;
                x*=10;
                x+=n[j+1];
                //cout<<n3[i][j]<<" "<<i<<" "<<j<<endl;
            }
        }
        for(int i=1;i<=n2;i++)
        {
            for(int j=1;j<=m&&j<=i;j++)
            {
                for(int k=1;k<=i;k++)
                {
                    if(f[i][j]<f[k-1][j-1]*n3[k][i])
                    {
                        f[i][j]=f[k-1][j-1]*n3[k][i];
                        //cout<<f[i][j];
                        son[i][j]=k-1;//记录分割点
                    }
                    
                }
            }
        }
        cout<<f[n2][m]<<endl;
        if(m==n2)//特判,防止输出紊乱
            for(int i=1;i<=n2;i++)
                cout<<n[i]<<" ";
        else printf1(n2,m);
        cout<<endl;
    }
}
 石子合并

题目描述

在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆最大得分.

输入格式

数据的第1行试正整数N,1≤N≤2000,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式

输出共1行,最大得分

样例

样例输入

4
4 4 5 9

样例输出

54
最终一堆一定是前一次合并后,剩下的两堆相加的最优解。

状态转移方程
设t[i,j]表示从第i堆到第j堆石子数总和。
Fmax(i,j)表示将从第i堆石子合并到第j堆石子的最大的得分
Fmin(i,j)表示将从第i堆石子合并到第j堆石子的最小的得分(看题意要求没)

附上代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m[4001],m1[4001][4001],f[4001][4001],x,ma;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>m[i];
    }
    for(int i=1;i<=n;i++)
    {
        m[i+n]=m[i];
    }
    for(int i=1;i<=2*n-1;i++)
    {
        x=m[i];
        for(int j=i+1;j<=2*n-1;j++)
        {
            x+=m[j];
            m1[i][j]=x;
        }
    }
    for(int i=2*n-1;i>=1;i--)
    {
        for(int j=i;j<=2*n-1;j++)
        {
            f[i][j]=max(f[i+1][j],f[i][j-1])+m1[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(ma<f[i][i+n-1])ma=f[i][i+n-1];
    }
    cout<<ma;
}



 

猜你喜欢

转载自www.cnblogs.com/fengwu2005/p/11274738.html