【AcWing 1068. 环形石子合并】区间dp

题目链接

题意:

将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。

规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数 n 及每堆的石子数,并进行如下计算:

选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
选择一种合并石子的方案,使得做 n−1 次合并得分总和最小

分析:

这个题是石子合并的升级版,那么现在问题就落到了怎么将这个题转化到石子合并的问题上,那么就会想到将这n个序列复制一遍放在这个序列的后面,然后再进行一次石子合并,从长度为n的序列中找出答案即可,现在请看代码:

#include
#include
using namespace std;
const int N = 410;

int a[N],s[N],f1[N][N],f2[N][N];

int main(){
    int n;
    cin>>n;
    memset(f1,0x3f,sizeof f1);
    for(int i=1;i<=2*n;i++) f1[i][i] = 0;
    for(int i=1;i<=n;i++) cin>>a[i],s[i] = s[i-1] + a[i];
    for(int i=n+1;i<=2*n;i++) a[i] = a[i-n],s[i] = s[i-1] + a[i];
    for(int len=2;len<=n;len++){
        for(int i=1;i<=2*n-len+1;i++){
            int j = i + len - 1;
            for(int k=i;k<j;k++){
                f1[i][j] = min(f1[i][j],f1[i][k]+f1[k+1][j]+s[j]-s[i-1]);
                f2[i][j] = max(f2[i][j],f2[i][k]+f2[k+1][j]+s[j]-s[i-1]);
            }
        }
    }
    int ans2 = 0,ans1 = 0x3f3f3f3f;
    for(int i=1;i<=n+1;i++){
        ans1 = min(ans1,f1[i][i+n-1]);
        ans2 = max(ans2,f2[i][i+n-1]);
    }
    cout<<ans1<<endl<<ans2<<endl;
    return 0;
}

你可能感兴趣的:(算法题目,动态规划,动态规划,算法,竞赛)