【p1880】石子合并
来源:互联网 发布:JS授权系统源码 编辑:程序博客网 时间:2024/05/15 08:28
https://www.luogu.org/problem/show?pid=1880
1.
先从线性的开始吧。
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
样例:
[输入]
3
1 2 3
7
13 7 8 16 21 4 18
[输出]
9
239
由于是相邻两个进行合并,贪心算法并不是最优的算法(比如第二组)。
可以用区间DP来解决。
状态:设f[i][j]表示从第i堆到第j堆得石子中所需的最小代价
决策:在区间[i,j)中枚举一个k,则f[i][j] = min(f[i][k] + f[k+1][j] + s[i][j]);
其中s[i][j]表示这段区间的石子数。
用i枚举上界,用l来枚举区间长度,那么j = i + l;
k来枚举区间中选定的那一堆。
时间复杂度为O(n^3)
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<cstdio>#define N 50#define INF 100000using namespace std;inline int read(){ char c; int res,flag = 0; while((c = getchar())>'9'||c<'0') if(c=='-')flag = 1; res = c - '0'; while((c = getchar())>='0'&&c<='9') res =(res<<3)+(res<<1) + c - '0'; return flag?-res:res;}int sum[N+1];int f[N][N];int main(){ int n = read(); sum[0] = 0; for (int i =1;i<=n;i++){ int x; x = read(); sum[i] = sum[i-1] + x; } memset(f,0,sizeof(f)); for (int l=2;l<=n;l++){ for (int i=1;i<=n-l+1;i++){//要加1 int j =i + l -1 ; f[i][j] = INF; for (int k =i;k<j;k++) f[i][j] = min(f[i][j],f[i][k]+f[k+1][j] + sum[j] - sum[i-1]); } } cout<<f[1][n];}
可以用平行四边形优化来降低时间复杂度。
来自百度百科的解释
如果对于任意的a1≤a2 < b1≤b2,
有m[a1,b1]+m[a2,b2]≤m[a1,b2]+m[a2,b1],那么m[i,j]满足四边形不等式。设m[i,j]表示动态规划的状态量。
m[i,j]有类似如下的状态转移方程:
m[i,j]=min{m[i,k]+m[k,j]}(i≤k≤j)
m满足四边形不等式是适用这种优化方法的必要条件
对于一道具体的题目,我们首先要证明它满足这个条件,一般来说用数学归纳法证明,根据题目的不同而不同。
通常的动态规划的复杂度是O(n^3),我们可以优化到O(n^2)
定义s(i,j)为函数m(i,j)对应的使得m(i,j)取得最小值的k值。
我们可以证明,s[i,j-1]≤s[i,j]≤s[i+1,j]
那么改变状态转移方程为:
m[i,j]=min{m[i,k]+m[k,j]}(s[i,j-1]≤k≤s[i+1,j])
复杂度分析:不难看出,复杂度决定于s的值,以求m[i,i+L]为例,
(s[2,L+1]-s[1,L])+(s[3,L+2]-s[2,L+1])…+(s[n-L+1,n]-s[n-L,n-1])=s[n-L+1,n]-s[1,L]≤n
所以总复杂度是O(n)
以上所给出的状态转移方程只是一种比较一般的,其实,很多状态转移方程都满足四边形不等式优化的条件。
解决这类问题的大概步骤是:
证明w满足四边形不等式,这里w是m的附属量,形如m[i,j]=opt{m[i,k]+m[k,j]+w[i,j]},此时大多要先证明w满足条件才能进一步证明m满足条件
证明m满足四边形不等式
证明s[i,j-1]≤s[i,j]≤s[i+1,j]
w[a,c]+w[b,d]<=w[b,c]+wa,d 就称其满足凸四边形不等式
或打表观察w[i][j+1]-w[i][j]关于i的表达式,如果关于i递减,则w满足凸四边形不等式
如果一个函数w[i][j],满足 w[i’][j]<=w[i][j’] i<=i’<=j<=j’ 则称w关于区间包含关系单调
如果w同时满足四边形不等式和区间单调关系,则dp也满足四边形不等式
2.
如果石子是环形排列的,由于k是我们枚举的,可以保证在1到n里,但是K+1和j可能会超出范围。因此f[i][j] = min(f[i][k]+f[(i+k+1)%n][j-k-1]+sum[i][i+j]),其中对于sum[i][i+j]
参考:http://blog.csdn.net/bnmjmz/article/details/41308919
http://blog.csdn.net/acdreamers/article/details/18039073
https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1880
- [洛谷P1880]石子合并
- 洛谷P1880 石子合并
- 【luogu】 P1880 石子合并
- 【洛谷】P1880 石子合并
- 洛谷P1880 石子合并
- 洛谷 P1880 石子合并
- P1880 石子合并
- 【洛谷P1880】合并石子
- 洛谷p1880石子合并
- 洛谷P1880 石子合并
- p1880石子合并 题解
- 【p1880】石子合并
- 【P1880】石子合并(环形)
- (ssl1597)P1880 石子合并问题
- 洛谷 p1880 石子合并 区间dp
- 洛谷 P1880 石子合并 区间dp
- 石子-石子合并
- 石子合并
- Python模块
- 数据库学习1
- Codeforces Round #442 (Div. 2) F. Ann and Books(莫队)
- 安卓编程之安卓开发初体验
- [noip2013]花匠 题解
- 【p1880】石子合并
- CMD 创建一个空文件
- 实现矩阵从外围到内依次输出
- 下拉列表左右选择(js代码)
- 模板的声明和实现为何要放在头文件中?
- 【Unity】多边形雷达图
- (for..in)、Object.keys()和Object.getOwnPropertyNames(),for...of
- Java(5):基础:String、StringBuffer和StringBuilder的区别
- linux小练习5