[08山东省选]2298 石子合并 即POJ 1738 An old Stone Game
在一个操场上摆放着一排N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将N堆石子合并成一堆的最小得分。
第一行是一个数N。
以下N行每行一个数A,表示石子数目。
共一个数,即N堆石子合并成一堆的最小得分。
4
1
1
1
1
8
对于 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堆石子合并成一堆的总花费最小(或最大)。
直线取石子问题的平行四边形优化:
#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 ;
}
#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 ;
}
最新文章
- phylogeny analysis
- JavaScript表单处理(上)
- JavaWeb学习总结(七)—HttpServletRequest
- 【iOS开发】添加子控件方式(懒加载,GCC)
- 结对作业1----基于flask框架的四则运算生成器
- Delphi的RzDbgrid改变某行的背景色
- Python【第三课】 函数基础
- 安装linux
- new
- docker部署Javaweb环境数据库连接问题
- Quartz.Net进阶之四:CronTrigger 详述
- day6 六、元组、字典、集合的基本操作和内置方法
- Values &; Reference:值和引用
- odoo明细表汇总数据
- Linux下烧写工具DNW和USB驱动安装(一)
- 使用redis镜像
- 不到 200 行代码,教你如何用 Keras 搭建生成对抗网络(GAN)【转】
- java通过java.net.URL发送http请求调用接口
- Keepalived stable tarball
- chest