• 题解报告: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 }
  • 相关阅读:
    毕业设计每日博客——第八周4
    毕业设计每日博客——第八周3
    毕业设计每日博客——第八周2
    毕业设计每日博客——第八周1
    毕业设计每日博客——第七周5
    毕业设计每日博客——第七周4
    毕业设计每日博客——第七周3
    毕业设计每日博客——第七周2
    毕业设计每日博客——第七周1
    毕业设计每日博客——第六周5
  • 原文地址:https://www.cnblogs.com/acgoto/p/9624438.html
Copyright © 2020-2023  润新知