石子合并

P1880 [NOI1995]石子合并

题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

状态转移方程的定义: dp( l , r ) 的含义是合并l - r这段区间的石子所能获得的最大值,那么,合并第i颗石子的最大值就是左边的加右边的

环形的……

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 205;
int Max[MAXN][MAXN],Min[MAXN][MAXN];
int sum[205];
int dp(int l,int r) {   
    if(Max[l][r]) {
        return Max[l][r];
    }
    if(l == r) {
        return 0;
    }
    int t1 = 0;
    for(int i = l; i < r; i++) {
        t1 = max(dp(l, i) + dp(i + 1, r) + sum[r] - sum[l - 1],t1);     //为什么要加上 sum[r] - sum[l - 1] 
                                                                        //因为合并一次,要消耗的值就是l - r的石子中数之和 
    }
    return Max[l][r] = t1;
}
int dp2(int l ,int r) {
    if(Min[l][r]) {
        return Min[l][r];
    }
    if(l == r) {
        return Min[l][r] = 0;
    }
    int t1 = 999999;
    for(int i = l; i < r; i++) {
        t1 = min(dp2(l, i) + dp2(i + 1, r) + sum[r] - sum[l - 1],t1);
    }
    return Min[l][r] = t1;
}
int main() {
    int n;
    cin>>n;
    for(int i = 1; i<=n; i++) {
        cin>>sum[i];
        sum[i+n] = sum[i];
    }
    for(int i = 1; i <= 2*n; i++) sum[i] += sum[i - 1];
    dp2(1,2*n);
    dp(1,2*n);
    int minn = 999999,maxn = 0;
    for(int i = 1; i<=n; i++) {
        minn = min(Min[i][n+i-1],minn);
    }
    cout<<minn<<endl;
    for(int i = 1; i<=n; i++) {
        maxn = max(Max[i][n+i-1],maxn);
    }
    cout<<maxn;
    return 0;
}
posted @ 2018-01-20 16:12  WenOI  阅读(258)  评论(0编辑  收藏  举报
水波背景