2298 石子合并

2008年省队选拔赛山东

 时间限制: 1 s
 空间限制: 256000 KB
 题目等级 : 黄金 Gold
 查看运行结果
 
 
题目描述 Description

  在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

  试设计一个算法,计算出将N堆石子合并成一堆的最小得分。

输入描述 Input Description

  第一行是一个数N。

  以下N行每行一个数A,表示石子数目。

输出描述 Output Description

  共一个数,即N堆石子合并成一堆的最小得分。

样例输入 Sample Input

4

1

1

1

1

样例输出 Sample Output

8

数据范围及提示 Data Size & Hint

对于 30% 的数据,1≤N≤100

对于 60% 的数据,1≤N≤1000

对于 100% 的数据,1≤N≤40000

对于 100% 的数据,1≤A≤200

分类标签 Tags 点此展开

 

题意: 石子合并问题, 将相邻两堆石子合并, 每次得分是合并成新的一堆石子个数, 最后累加最小值.

解题思路:

1. 这类题目一开始想到是DP, 设dp[i][j]表示第i堆石子到第j堆石子合并最小得分.

状态方程: dp[i][j] = min(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);

sum[i]表示第1到第i堆石子总和. 递归记忆化搜索即可.

2. 不过此题有些不一样, 1<=n<=50000范围特大, dp[50000][50000]开不到这么大数组.

问题分析:

(1). 假设我们只对3堆石子a,b,c进行比较, 先合并哪2堆, 使得得分最小.

score1 = (a+b) + ( (a+b)+c )

score2 = (b+c) + ( (b+c)+a )

再次加上score1 <= score2, 化简得: a <= c, 可以得出只要a和c的关系确定,

合并的顺序也确定.

(2). GarsiaWachs算法, 就是基于(1)的结论实现.找出序列中满足stone[i-1] <=

stone[i+1]最小的i, 合并temp = stone[i]+stone[i-1], 接着往前面找是否

有满足stone[j] > temp, 把temp值插入stone[j]的后面(数组的右边). 循环

这个过程一直到只剩下一堆石子结束.

(3). 为什么要将temp插入stone[j]的后面, 可以理解为(1)的情况

从stone[j+1]到stone[i-2]看成一个整体 stone[mid],现在stone[j],

stone[mid], temp(stone[i-1]+stone[i-1]), 情况因为temp < stone[j],

因此不管怎样都是stone[mid]和temp先合并, 所以讲temp值插入stone[j]

的后面是不影响结果.

#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
#define N 50010
int t=,n,a[N];
ll ans=;
void work(int k){
int tmp=a[k-]+a[k];
ans+=tmp;
for(int i=k;i<t-;i++)
a[i]=a[i+];
t--;
int j=;
for(j=k-;j>&&a[j-]<tmp;j--)
a[j]=a[j-];
a[j]=tmp;
while(j>=&&a[j]>=a[j-]){
int d=t-j;
work(j-);
j=t-d;
}
}
int main(){
scanf("%d",&n);
for(int i=;i<n;i++)
scanf("%d",&a[i]);
for(int i=;i<n;i++){
a[t++]=a[i];
while(t>=&&a[t-]<=a[t-])
work(t-);
}
while(t>)
work(t-);
printf("%lld\n",ans);
return ;
}

-----------------------------------------------------------------------------------------------------------------------------------------------

华丽的分割线

------------------------------------------------------------------------------------------------------------------------------------------------

以下是石子合并系列问题的常用思路,现附上。(以下大部分来自网络,代码未经测试,看看思路就好)

石子合并问题是最经典的DP问题。首先它有如下3种题型:

(1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

分析:当然这种情况是最简单的情况,合并的是任意两堆,直接贪心即可,每次选择最小的两堆合并。本问题实际上就是哈夫曼的变形。

(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

分析:我们熟悉矩阵连乘,知道矩阵连乘也是每次合并相邻的两个矩阵,那么石子合并可以用矩阵连乘的方式来解决。
设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式:
 

直线取石子问题的平行四边形优化:

#include <iostream>
#include <string.h>
#include <stdio.h> using namespace std;
const int INF = << ;
const int N = ; int dp[N][N];
int p[N][N];
int sum[N];
int n; int getMinval()
{
for(int i=; i<=n; i++)
{
dp[i][i] = ;
p[i][i] = i;
}
for(int len=; len<n; len++)
{
for(int i=; i+len<=n; i++)
{
int end = i+len;
int tmp = INF;
int k = ;
for(int j=p[i][end-]; j<=p[i+][end]; j++)
{
if(dp[i][j] + dp[j+][end] + sum[end] - sum[i-] < tmp)
{
tmp = dp[i][j] + dp[j+][end] + sum[end] - sum[i-];
k = j;
}
}
dp[i][end] = tmp;
p[i][end] = k;
}
}
return dp[][n];
} int main()
{
while(scanf("%d",&n)!=EOF)
{
sum[] = ;
for(int i=; i<=n; i++)
{
int val;
scanf("%d",&val);
sum[i] = sum[i-] + val;
}
printf("%d\n",getMinval());
}
return ;
}
 
(3)问题(2)的是在石子排列是直线情况下的解法,如果把石子改为环形排列,又怎么做呢?
分析:状态转移方程为:
其中有:
#include <iostream>
#include <string.h>
#include <stdio.h> using namespace std;
const int INF = << ;
const int N = ; int mins[N][N];
int maxs[N][N];
int sum[N],a[N];
int minval,maxval;
int n; int getsum(int i,int j)
{
if(i+j >= n) return getsum(i,n-i-) + getsum(,(i+j)%n);
else return sum[i+j] - (i> ? sum[i-]:);
} void Work(int a[],int n)
{
for(int i=;i<n;i++)
mins[i][] = maxs[i][] = ;
for(int j=;j<n;j++)
{
for(int i=;i<n;i++)
{
mins[i][j] = INF;
maxs[i][j] = ;
for(int k=;k<j;k++)
{
mins[i][j] = min(mins[i][j],mins[i][k] + mins[(i+k+)%n][j-k-] + getsum(i,j));
maxs[i][j] = max(maxs[i][j],maxs[i][k] + maxs[(i+k+)%n][j-k-] + getsum(i,j));
}
}
}
minval = mins[][n-];
maxval = maxs[][n-];
for(int i=;i<n;i++)
{
minval = min(minval,mins[i][n-]);
maxval = max(maxval,maxs[i][n-]);
}
} int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=;i<n;i++)
scanf("%d",&a[i]);
sum[] = a[];
for(int i=;i<n;i++)
sum[i] = sum[i-] + a[i];
Work(a,n);
printf("%d %d\n",minval,maxval);
}
return ;
}
可以看出,上面的(2)(3)问题的时间复杂度都是O(n^3),由于过程满足平行四边形法则,故可以进一步优化到O(n^2)。

[08山东省选]2298 石子合并 即POJ 1738 An old Stone Game的更多相关文章

  1. 题解报告:poj 1738 An old Stone Game(区间dp)

    Description There is an old stone game.At the beginning of the game the player picks n(1<=n<=5 ...

  2. POJ 1738 An old Stone Game(石子合并 经典)

    An old Stone Game Time Limit: 5000MS   Memory Limit: 30000K Total Submissions: 3672   Accepted: 1035 ...

  3. POJ 1738 石子合并2 GarsiaWachs算法

    石子合并(GarsiaWachs算法) 只能用该算法过!!! 详解看代码 //#pragma comment(linker, "/STACK:167772160")//手动扩栈~~ ...

  4. ny737 石子合并(一) 总结合并石子问题

    描述: 在一个圆形操场的四周摆放着n 堆石子.现要将石子有次序地合并成一堆. 规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分. 试设计一个算法,计算出将n堆石子合并 ...

  5. 【BZOJ 3229】 3229: [Sdoi2008]石子合并 (GarsiaWachs算法)

    3229: [Sdoi2008]石子合并 Description 在一个操场上摆放着一排N堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合 ...

  6. RQNOJ 490 环形石子合并

    题目链接:https://www.rqnoj.cn/problem/490 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一 ...

  7. 石子合并[DP-N3]

    题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...

  8. 51Nod 1021 石子合并 Label:Water DP

    N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价.   例如: 1 2 3 4,有 ...

  9. BZOJ 3229: [Sdoi2008]石子合并

    3229: [Sdoi2008]石子合并 时间限制: 3 Sec  内存限制: 128 MB提交: 497  解决: 240[提交][][] 题目描述 在一个操场上摆放着一排N堆石子.现要将石子有次序 ...

随机推荐

  1. nginx $document_uri 参数使用

    $document_uri  表示访问的url 现在我的需求是,访问 www.abc.com  请求到 www.abc.com/abc/在nginx配置文件中加入 if ($document_uri ...

  2. DB2中查询前十数据的sql

    select * from A fetch first 10 rows only

  3. this的四种绑定规则总结

    一.默认绑定 1.全局环境中,this默认绑定到window 2.函数独立调用时,this默认绑定到window console.log(this === window);//true functio ...

  4. 【Hadoop】Hadoop MR 如何实现倒排索引算法?

    1.概念.方案 2.代码示例 InverseIndexOne package com.ares.hadoop.mr.inverseindex; import java.io.IOException; ...

  5. selenium模拟键盘操作

    单键 /** * 模拟键盘回车事件 * @throws AWTException */ public void KeyEventEnter() throws AWTException { Robot ...

  6. JAVA泛化及为什么需要泛化

    泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 比如,有一种类型为List,此时该List可以是任意类型的列表,如Integer,String等等. 如果把List类型改为List ...

  7. HttpURLConnection读取网页文件

    一.关键代码 public class MainActivity extends Activity { TextView content; ; Handler handler = new Handle ...

  8. flashplayer

    http://www.adobe.com/support/flashplayer/downloads.html

  9. Oracle case when then else end的两种用法

    查询表结构 SELECT T.COLUMN_ID, T.COLUMN_NAME, (CASE WHEN (T.DATA_TYPE = 'VARCHAR2' OR T.DATA_TYPE = 'RAW' ...

  10. 原生JS实现的h5小游戏-植物大战僵尸

    代码地址如下:http://www.demodashi.com/demo/12755.html 项目介绍 本项目是利用原生js实现的h5小游戏-植物大战僵尸,主要结合了一下自己对于h5小游戏的理解,结 ...