赞
踩
时限:1000ms 内存限制:10000K 总时限:3000ms
描述
在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,读入石子堆数n及每堆的石子数(<=20)。选择一种合并石子的方案,使得做n-1次合并,得分的总和最小; 比如有4堆石子:4 4 5 9 则最佳合并方案如下:
4 4 5 9 score: 0
8 5 9 score: 8
13 9 score: 8 + 13 = 21
22 score: 8 + 13 + 22 = 43
输入
可能有多组测试数据。 当输入n=0时结束! 第一行为石子堆数n(1<=n<=100); 第二行为n堆的石子每堆的石子数,每两个数之间用一个空格分隔。
输出
合并的最小得分,每个结果一行。
输入样例
4 4 4 5 9 0
输出样例
43
解析:这个题,,我原以为它是和矩阵连乘乘积的那个题一样,后来发现有两点不同。第一点是对于矩阵连乘的题目,num=arr[i][k]+arr[k+1][j]+row[i]*col[k]*col[j],这里一个新的arr的数是两个子项代价加起来,然后再加这两个子项算的代价,而这个摆石子是算的两个子项加起来,然后再加这两个的和。。。好吧我有点卡住了,我不知道怎么把以前的代价加进去。
像矩阵连乘那样,把对角线都设为0,算当前节点m [ i ] [ j ] 时,取其中间节点h , 然后算m[i[[j]=m[i][h]+m[h+1][j]+x,这个x为把这两个子节点连起来的代价,矩阵连乘是将 i , h , j 的维数乘起来,这里的话,应该是将当前两堆直接加起来,举个例子,如:
对于数据7,3,4,8,其产生的矩阵就应该为:
0 10 21 43
0 0 7 22
0 0 0 12
0 0 0 0
上面为第一次算法后,第一次的m[i][j]就等于stone[i]+stone[j],当进行到m[1][3]和m[2][4]时,如m[1][3]=min( m[1][1]+m[2][3]+stone[1]+stone[2]+stone[3] ,m[1][2]+m[3][3]+stone[1]+stone[2]+stone[3]),m[2][4]=min( m[2][2]+m[3][4]+stone[2]+stone[3]+stone[4] ,m[2][3]+m[4][4]+stone[1]+stone[2]+stone[3] )。
事实证明,写博客有利于整理思路!啊哈哈哈,详见代码:
- #include <iostream>
- #include<stdio.h>
- #include<string.h>
- using namespace std;
- int n;
- int stone[100];
- int m[100][100],s[100][100];//s是记录路径的
- int num(int m,int n)
- {
- int res=0;
- for(int i=m;i<=n;i++)
- res+=stone[i];
- return res;
- }
- int main()
- {
- while(~scanf("%d",&n)&&n)
- {
- memset(stone,0,sizeof(stone));
- memset(m,0,sizeof(m));
- memset(s,0,sizeof(s));
- for(int i=1;i<=n;i++)
- {
- cin>>stone[i];
- }
- for(int r=1;r<n;r++)
- {
- for(int i=1;i<=n-r;i++)
- {
- m[i][i+r]=m[i][i]+m[i+1][i+r]+num(i,i+r);
- s[i][i+r]=i;
- int j;
- for(j=i+1;j<i+r;j++)
- {
- if(m[i][j]+m[j+1][i+r]+num(i,i+r)<m[i][i+r])
- {
- m[i][i+r]=m[i][j]+m[j+1][i+r]+num(i,i+r);
- s[i][i+r]=j;
- }
- }
- }
- }
- cout<<m[1][n]<<endl;
- /*
- for(int i=1;i<=n;i++)
- {
- for(int j=1;j<=n;j++)
- cout<<m[i][j]<<" ";
- cout<<endl;
- }
- */
- }
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
不过上面这个是仿照矩阵连乘写的,本题中石子是环形的,那么那个n维矩阵每一个元素都要用到了,循环里面也需加一点东西就可以。那么对于主层循环,
for(int r=1;r<n;r++)
{
for(int i=1;i<=n-r;i++)
{
m[i][i+r]=m[i][i]+m[i+1][i+r]+num(i,i+r);
s[i][i+r]=i;
for(int j=i+1;j<i+r;j++)
间隔r自然是从1到n-1不变的,对于每一个开头的 i,应该改为 i<=n 即可,算法是m[i][(i+r)%n],既可以是m[1][3],也可以是m[3][1],且这两种算法是不同的,对应的 j 应该是1 2 3和3 4 1,用统一来表示就要取余,详见代码:
- #include <iostream>
- #include<stdio.h>
- #include<string.h>
- using namespace std;
- int n;
- int stone[100];
- int m[100][100],s[100][100];//s是记录路径的
- int num(int s,int t)
- {
- int res=0;
- if(s<=t)
- {
- for(int i=s;i<=t;i++)
- res+=stone[i];
- }
- else
- {
- for(int i=s;i<n;i++)
- res+=stone[i];
- for(int i=0;i<=t;i++)
- res+=stone[i];
- }
- return res;
- }
- int main()
- {
- while(~scanf("%d",&n)&&n)
- {
- memset(stone,0,sizeof(stone));
- memset(m,0,sizeof(m));
- memset(s,0,sizeof(s));
- for(int i=0;i<n;i++)
- {
- cin>>stone[i];
- }
- for(int r=1;r<n;r++)
- {
- for(int i=0;i<n;i++)
- {
- m[i][(i+r)%n]=m[i][i]+m[(i+1)%n][(i+r)%n]+num(i,(i+r)%n);
- s[i][(i+r)%n]=i;
- int k=r-1;
- for(int j=(i+1)%n;k>=0;j=(j+1)%n,k--)
- {
- if(m[i][j%n]+m[(j+1)%n][(i+r)%n]+num(i,(i+r)%n)<m[i][(i+r)%n])
- {
- m[i][(i+r)%n]=m[i][j%n]+m[(j+1)%n][(i+r)%n]+num(i,(i+r)%n);
- s[i][(i+r)%n]=j;
- }
- }
- }
- }
- int x=1000;
- for(int i=0;i<n;i++)
- {
- if(m[i][(i+n-1)%n]<x)
- x=m[i][(i+n-1)%n];
- }
- cout<<x<<endl;
- }
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。