zoukankan      html  css  js  c++  java
  • 题解报告:NYOJ #737 石子合并(一)(区间dp)

    描述    

    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

    输入

    有多组测试数据,输入到文件结束。
    每组测试数据第一行有一个整数n,表示有n堆石子。
    接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开输出输出总代价的最小值,占单独的一行

    样例输入

    3
    1 2 3
    7
    13 7 8 16 21 4 18

    样例输出

    9
    239
    解题思路:经典区间dp!石子合并问题!定义dp[i][j]表示将区间[i,j]合并后得到的最小代价,易想到相邻先两两合并,再三三合并....,直到将整个区间合并完成,dp[1][n]就是要求的最小代价。
    状态转移方程为dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]),其中sum[j]-sum[i-1](预处理前缀和)为将区间[i,j]合并得到的代价,k为断点的枚举。时间复杂度为O(n^3)。

    AC代码一(340ms):
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=205;
     4 int n,a[maxn],sum[maxn],dp[maxn][maxn];
     5 int main(){
     6     while(~scanf("%d",&n)){
     7         memset(dp,0x3f,sizeof(dp));
     8         memset(sum,0,sizeof(sum));
     9         for(int i=1;i<=n;++i)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i],dp[i][i]=0;//初始状态dp[i][i]表示当前每一堆的代价为0
    10         for(int len=1;len<=n;++len){//区间长度
    11             for(int i=1;i<=n-len;++i){//区间起点
    12                 int j=i+len;//区间终点
    13                 for(int k=i;k<j;++k)//断点k把(i,j)分成2堆,dp[i][j]为原来两堆各自的代价和再加上合并的两堆得到的代价之和
    14                     dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
    15             }
    16         }
    17         printf("%d
    ",dp[1][n]);
    18     }
    19     return 0;
    20 }

    AC代码二(0ms):GarsiaWachs算法,时间复杂度为0(n^2)。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 using namespace std;
     5 const int maxn=205;
     6 const int inf=0x7fffffff;//2147483647
     7 int n,m,t,ans,stone[maxn];
     8 void dfs(int k){
     9     int tmp=stone[k-1]+stone[k];
    10     ans+=tmp;t--;
    11     for(int i=k;i<t;++i)stone[i]=stone[i+1];//元素左移,表示删掉了一个元素
    12     int j=0;k--;
    13     for(j=k;stone[j-1]<tmp;--j)stone[j]=stone[j-1];//元素右移,找到第一个满足条件的j
    14     stone[j]=tmp;//将tmp插到j后面
    15     while(j>=3&&stone[j-2]<=stone[j]){//继续向前查找是否还有满足条件的情况
    16         int d=t-j;//保存当前t离操作点的距离d
    17         dfs(j-1);//合并第j-1堆和第j-2堆石子
    18         j=t-d;//设置新的操作点j
    19     }
    20 }
    21 int main(){
    22     while(~scanf("%d",&n)){
    23         for(int i=1;i<=n;++i)scanf("%d",&stone[i]);
    24         t=2,ans=0;stone[0]=stone[n+1]=inf;//左右边界设置成无穷
    25         for(int i=2;i<=n;++i){
    26             stone[t++]=stone[i];
    27             while(t>3&&stone[t-3]<=stone[t-1])dfs(t-2);//表示当前至少有3堆石子,并且满足stone[k-1]<=stone[k+1],其中k=t-2,就合并第t-3和第t-2堆石子
    28         }
    29         while(t>2)dfs(t-1);//如果剩下的堆数至少为3-1=2堆,则继续合并,直至剩下一堆石子
    30         printf("%d
    ",ans);
    31     }
    32     return 0;
    33 }
  • 相关阅读:
    洛谷 1339 最短路
    洛谷 1330 封锁阳光大学 图论 二分图染色
    洛谷 1262 间谍网络 Tarjan 图论
    洛谷 1373 dp 小a和uim之大逃离 良心题解
    洛谷 1972 莫队
    洛谷 2158 数论 打表 欧拉函数
    洛谷 1414 数论 分解因数 水题
    蒟蒻的省选复习(不如说是noip普及组复习)————连载中
    关于筛法
    关于整数划分的几类问题
  • 原文地址:https://www.cnblogs.com/acgoto/p/9624438.html
Copyright © 2011-2022 走看看