zoukankan      html  css  js  c++  java
  • 石子合并问题

            石子合并问题

    题目链接: http://acm.nankai.edu.cn/p1137.html

    题目大意: 有若干堆石头排成一个圆。。。给你一个n,表示石头堆数,接下来n个数,每个是对应堆的石头个数,每次操作能合并相邻堆的石头,该次得分就是合并后的石头数,不断进行这样的操作直到只剩下一堆,累计得分,然后输出最大得分和最小得分。

    题目思路: 刚拿到这题,一度陷入贪心的陷阱,但事实不是这样,可以举出反例 7、6、5、7、100 ,如果按照贪心的做法:

      1. 6、5 合并得 7、11、7、100  本次得分 11

      2. 7、11 合并得 18、7、100     本次得分 18

      3. 18、7 合并得 25、100          本次得分  25

      最少总分 54 ,剩 25、100

      最优解法:

      1. 7、6 合并   得 13

      2. 5、7 合并   得 12

      3. 12、13 合并 得 25

      最少得分 50 ,剩 25、100

      有了这个样例,所以题目不可以用贪心来解,可以考虑动态规划。。。

      做法代码中解释,有一份简单的比较好理解,一份难点的效率更高。

      

      1 /*
      2   经典DP模型:石子合并问题
      3   链接 :http://acm.nankai.edu.cn/p1137.html
      4 */
      5 
      6 #include<stdio.h>
      7 #include<string.h>
      8 #define N 110
      9 // 对于某种情况,求出该种情况的最大值
     10 int LookMax(int *a,int n)
     11 {
     12   int dp[N][N]; //dp[i][j] : 从第 i 堆开始合并到第 j 堆最大得分
     13   memset(dp,-1,sizeof(dp));  //初始化
     14   for(int i=0;i<n;i++)       //对于每一堆,只合并自身,也就是不进行任何操作,那么得分为0
     15     dp[i][i]=0;
     16   for(int i=0;i<n-1;i++)     //对于每一堆,合并自身和下一堆,最大得分为 :a[i]+a[j]
     17   {
     18     dp[i][i+1]=a[i]+a[i+1];
     19   }
     20   for(int r=2;r<n;r++)       //每一次循环计算 从第 i 堆出发,往右合并 r 堆的最大得分
     21   {
     22     for(int i=0;i<n-r;i++)   //对于不同起点分别计算
     23     {
     24       int j=i+r,sum=0;       //j:终点 sum:从 i 合并到 j 最后一次合并得分 sum,即第i堆到第j堆的石子总数
     25       for(int u=i;u<=j;u++)
     26         sum+=a[u];
     27       dp[i][j]=dp[i][i]+dp[i+1][j]+sum; //每次循环计算的便是 dp[i][j] ,初始化为自身 合并 下一堆到j的最大得分
     28       for(int u=i+1;u<j;u++)            //中间断点,最后合并是由两堆合并组成,那么u 便是枚举 两堆的分界点
     29       {
     30         int tmp=dp[i][u]+dp[u+1][j]+sum;//dp[i][j] 看作 dp[i][u] 和 dp[u+1][j] 的合并
     31         if(tmp>dp[i][j])                //如果这次分法更优,保存更优得分
     32           dp[i][j]=tmp;
     33       }
     34     }
     35   }
     36   return dp[0][n-1];                    //返回最大得分
     37 }
     38 
     39 //寻找最低得分和最高得分类似
     40 int LookMin(int *a,int n)
     41 {
     42   int dp[N][N];
     43   memset(dp,-1,sizeof(dp));
     44   for(int i=0;i<n;i++)
     45     dp[i][i]=0;
     46   for(int i=0;i<n-1;i++)
     47   {
     48     int j=i+1;
     49     dp[i][j]=a[i]+a[j];
     50   }
     51   int mint;
     52   for(int r=2;r<n;r++)
     53   {
     54     for(int i=0;i<n-r;i++)
     55     {
     56       int j=i+r,sum=0;
     57       for(int u=i;u<=j;u++)
     58         sum+=a[u];
     59       dp[i][j]=dp[i+1][j]+sum;
     60       for(int u=i+1;u<j;u++)
     61       {
     62         int tmp=dp[i][u]+dp[u+1][j]+sum;
     63         if(tmp<dp[i][j])
     64           dp[i][j]=tmp;
     65       }
     66     }
     67   }
     68   mint=dp[0][n-1];
     69   return mint;
     70 }
     71 int main()
     72 {
     73   int n;
     74   int a[N];
     75   while(scanf("%d",&n)!=EOF)
     76   {
     77     for(int i=0;i<n;i++)
     78     {
     79       scanf("%d",&a[i]);
     80     }
     81     // 题目中石子是连成一个圈的,为了简化问题,可以分解成n个起点,每种起点对应一种不形成圆的情况
     82     // 对于这 n 种情况,每种情况求最小和最大,最终再得到结果
     83     int mint=LookMin(a,n);
     84     int maxt=LookMax(a,n);
     85     // 循环变化数组
     86     for(int i=1;i<n;i++)
     87     {
     88       int tmp=a[0];
     89       for(int j=1;j<n;j++)
     90       {
     91         a[j-1]=a[j];
     92       }
     93       a[n-1]=tmp;
     94       tmp=LookMin(a,n);
     95       if(tmp<mint) mint=tmp;
     96       tmp=LookMax(a,n);
     97       if(tmp>maxt) maxt=tmp;
     98     }
     99     printf("%d
    %d
    ",mint,maxt);
    100   }
    101   return 0;
    102 }
    AC 代码
     1 #include<stdio.h>
     2 #include<string.h>
     3 #define N 110
     4 int max(int a,int b)
     5 {
     6   return a>b?a:b;
     7 }
     8 int min(int a,int b)
     9 {
    10   return a>b?b:a;
    11 }
    12 int main()
    13 {
    14   int n;
    15   int a[N];
    16   //这次解法,没有对于n个石子进行n次不同情况的分开判断,而是一次解决,也就是说往右数j堆时会跨越边界
    17   int sum[N][N]; // sum[i][j]: 从第 i 堆出发,往右再累计 j 堆的石子数
    18   int dp[N][N];  // dp[i][j] : 从第 i 堆出发,往右再合并 j 堆的最大得分
    19   int db[N][N];  // db[i][j] : 从第 i 堆出发,往右再合并 j 堆的最小得分
    20   while(scanf("%d",&n)!=EOF)
    21   {
    22     memset(sum,0,sizeof(sum));
    23     for(int i=0;i<n;i++)
    24     {
    25       scanf("%d",&a[i]);
    26       sum[i][0]=a[i];          // 从第 i 堆出发,往右再累计0堆,也就是只包括自身,那么石子数为自身石子数
    27     }
    28     for(int i=0;i<n;i++)       // 注意,这里的总和是可以跨越边界的!不同写法只要能实现功能即可
    29       for(int j=1;j<n;j++)
    30         sum[i][j]=sum[i][j-1]+a[(i+j)%n];
    31     for(int i=0;i<n;i++)       //从第 i 堆出发,往右再合并0堆,那么得分为0
    32       dp[i][0]=db[i][0]=0;
    33     for(int i=1;i<n;i++)       //往右再合并 i 堆,最多再合并 n-1 堆
    34     {
    35       for(int j=0;j<n;j++)     //出发点 : j
    36       {
    37         dp[j][i]=0;            //每次循环计算一个 dp[j][i]
    38         db[j][i]=100000000;
    39         for(int k=0;k<i;k++)
    40         {
    41           dp[j][i]=max(dp[j][i],dp[j][k]+dp[(j+k+1)%n][i-k-1]+sum[j][i]);
    42           /*
    43             dp[j][i] : 根据中间断点不同,可以分为k=0 到 k=i-1
    44             k=0 :  自身                     和   后面 i-1 堆        合并
    45             k=i-1: 自身和自身后面的i-1堆    和   最后 1 堆          合并
    46           */
    47           db[j][i]=min(db[j][i],db[j][k]+db[(j+k+1)%n][i-k-1]+sum[j][i]);
    48         }
    49       }
    50     }
    51     int maxt=0;
    52     int mint=100000000;
    53     //对于n个起点 ,寻找最大
    54     for(int i=0;i<n;i++)
    55     {
    56       if(dp[i][n-1]>maxt) maxt=dp[i][n-1];
    57       if(db[i][n-1]<mint) mint=db[i][n-1];
    58     }
    59     printf("%d
    %d
    ",mint,maxt);
    60   }
    61   return 0;
    62 }
    AC 代码改进
  • 相关阅读:
    远程登陆服务——SSH
    A web-based 3D modeling framework for a runner-gate design( 一种基于web的三维建模框架)
    Vue+Element+Echarts+Springboot+微信小程序开发(肿瘤医学项目)
    基于拖放布局的 Twitter Bootstrap 网站生成器
    Ubuntu下git使用华为云/gitee/github
    在 Ubuntu 18.04 上安装 Postman
    在Ubuntu 18.04上安装Git与入门教程
    转载:渲染层和逻辑层
    Ubuntu系统中安装Neo4j
    NosqlBooster连接数据库失败connect ECONNREFUSED 127.0.0.1:27017——mongodb连接失败
  • 原文地址:https://www.cnblogs.com/hchlqlz-oj-mrj/p/4898432.html
Copyright © 2011-2022 走看看