• LuoguP1880[NOI1995]石子合并


    Description:

    在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分。
    区间DP,状态转移方程dp[l][r] = max(dp[l][r],dp[l][k] + dp[k+1][r] + sum[r] - sum[l-1]) 其中,k为区间中间点,sum[] 维护前缀和
    区间动归状态转移方程及一般动规过程:

    for len:=1 to n-1 do    //区间长度
      for l:=1 to n-len do     //区间起点
        for k:=l to l+len-1 do     //区间中任意点
          dp[r,l+len]:=max{dp[l,k] + dp[k+1,l+len] + a[l,k] + a[k+1,i+len]
    

    区间长度len必须要放到第一层循环,来保证方程中状态dp[l,k]、dp[k+1,l+k]值在dp[l,l+k]之前就已计算出来。
    一开始不明白为什么要用堆数作为阶段,写了个类似于Floyd的DP

    for(int k = l;k < r;++k)
    	{
    		for(int l = 1;l < n << 1;++l){
    			for(int r = l + 1;r <= n << 1;++r){
    				dp1[l][r] = min(dp1[l][r],dp1[l][k] + dp1[k + 1][r] + sum[r] - sum[l-1]);
    				dp2[l][r] = max(dp2[l][r],dp1[l][k] + dp2[k + 1][r] + sum[r] - sum[l-1]);
    			}
    		}		
    	}
    

    但是编译没通过,因为k循环不好赋值,难以实现,因此还是枚举区间长度好了。
    https://www.cnblogs.com/Zforw/p/10617652.html

    Code

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #define N 110
    #define ll long long
    using namespace std;
    ll dp2[N << 1][N << 1],dp1[N << 1][N << 1],n,ans1,ans2;
    ll sum[N << 1],a[N << 1];
    int main() {
        scanf("%lld",&n);
        memset(dp1,0x3f,sizeof(dp1));
        for(int i = 1; i <= n; i++) {
            scanf("%lld",&a[i]);
            a[i + n] = a[i];
        }
        for(int i = 1;i <= n << 1;++i){
            sum[i] = sum[i-1] + a[i];
            dp2[i][i] = dp1[i][i] = 0;
        }
        for(int i = 1; i <= n;++i)
            dp2[i][i] = dp1[i][i] = 0;
        // 以合并的堆数为阶段
        for(int len = 2; len <= n;++len) {
            for(int l = 1; l <= (n << 1 | 1) - len;++l) {
                int r = len + l - 1;
                for(int k = l; k < r;++k) {
                    dp1[l][r] = min(dp1[l][r],dp1[l][k] + dp1[k + 1][r] + sum[r] - sum[l-1]);
                    dp2[l][r] = max(dp2[l][r],dp2[l][k] + dp2[k + 1][r] + sum[r] - sum[l-1]);
                }
            }
        }
        ans1 = 0x3f3f3f3f;
        for(int i = 1;i <= n;++i) ans1 = min(ans1,dp1[i][i + n - 1]);
        for(int i = 1;i <= n;++i) ans2 = max(ans2,dp2[i][i + n - 1]);
        printf("%lld
    %lld",ans1,ans2);
        return 0;
    }
    
    岂能尽如人意,但求无愧我心
  • 相关阅读:
    hibernate关联映射
    线程实现输出结果为100对(1,0)
    hibernate入门
    数据库面试sql
    [网络流24题] 方格取数问题
    [网络流24题] 飞行员配对方案问题
    [CTSC2014]企鹅QQ hash
    [JSOI2010]缓存交换 贪心 & 堆
    Linux相关——画图软件安装
    [NOIP2010] 引水入城 贪心 + 记忆化搜索
  • 原文地址:https://www.cnblogs.com/Zforw/p/10610645.html
Copyright © 2020-2023  润新知