博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DP优化——四边形不等式(实例)
阅读量:5326 次
发布时间:2019-06-14

本文共 6521 字,大约阅读时间需要 21 分钟。

hdu2829:

dp方程:f[m][n] = Min{f[m-1][k]+w[k+1][n]}。

令w[i][j](i<=j)表示(a[i]*a[i+1]+a[i]*a[i+2]+....+a[i]*a[j])+(a[i+1]*a[i+2]+a[i+1]=a[i+3]+....)+....+a[j-1]*a[j]——(1)

则(1)式可以表示为[(a[i]+a[i+1]+...+a[j])*(a[i]+a[i+1]+...+a[j])-a[i]^2-a[i+1]^2-....-a[j]^2] / 2

即(1) = [(sum1[j]-sum1[i-1])^2-(sum2[j]-sum2[i-1])]/2。

其中sum1[i]表示从0到i元素的和,sum2[i]表示从0到i元素的平方的和,可以用O(n)的时间预处理。

现在我们固定j,看一看函数 w[i][j+1]-w[i][j] 随i增加的增减性,很明显如果i加1的话,w[i][j+1]比w[i][j]减少的要多,所以该函数递减,所以w为凸;

而区间包含关系是很显然的,所以w函数满足四边形不等式,所以可以推得f[m][n]也满足四边形不等式。

决策变量范围:s[m-1][n]<=s[m][n]<=s[m][n+1]。实际上这个地方的单调性凭感觉也是很好发现的,试想一下,如果你亲自去划分(而不是程序去划分)的话,你也会这么做的。

#include 
#include
#include
usingnamespace std; constint N =1010; typedef longlong llg; int n, m, a[N], sum1[N], sum2[N], s[N][N]; llg f[N][N]; void dp() {
int i, j, k, a, b; llg tmp; memset(f, -1, sizeof(f)); for(i =0; i <= n; i++) {
tmp = sum1[i]; f[0][i] = (tmp*tmp - sum2[i]) /2; s[0][i] =0; } for(i =1; i <= m; i++) for(j = n; j >= i; j--) {
s[i][n+1] = n; a = s[i-1][j]; b = s[i][j+1]; for(k = a; k <= b; k++) {
tmp = sum1[j] - sum1[k]; tmp = f[i-1][k]+(tmp*tmp - (sum2[j]-sum2[k])) /2; if(f[i][j]==-1|| f[i][j]>tmp) {
f[i][j] = tmp; s[i][j] = k; } } } } int main() {
int i; while(scanf("%d%d", &n, &m) != EOF) {
if(n==0&& m==0) break; sum1[0] = sum2[0] =0; for(i =1; i <= n; i++) {
scanf("%d", a+i); sum1[i] = sum1[i-1] + a[i]; sum2[i] = sum2[i-1] + a[i]*a[i]; } dp(); printf("%lld\n", f[m][n]); } return0; }

 

hdu3480:

需要先排一下序,dp方程和上面一个题差不多,就是w不同,w[i][j] = (a[j]-a[i])*(a[j]-a[i])

然后w函数的区间包含关系是很显然的,然后对于w[i][j+1]-w[i][j],固定j,很明显由于平方关系w[i][j+1]减小的比较快,所以单调递减;所以满足凸特性,所以满足四边形不等式。。所以。。。贴代码~~注意需要滚动数组,要不用long long会MLE的,即使内存给的很大。。

#include 
#include
#include
#include
usingnamespace std; constint N =10010; constint M =5010; typedef longlong llg; int n, m, flag, s[2][N]; llg a[N], f[2][N]; void dp() {
int i, j, k, head, tail; flag =1; llg tmp; memset(f, -1, sizeof(f)); for(i =1; i <= n; i++) {
f[flag][i] = (a[i]-a[1])*(a[i]-a[1]); s[flag][i] =0; } flag ^=1; for(i =2; i <= m; i++) {
s[flag][n+1] = n; for(j =1; j <= n; j++) f[flag][j] =-1; for(j = n; j >= i; j--) {
head = s[flag^1][j]; tail = s[flag][j+1]; for(k = head; k <= tail; k++) {
tmp = (a[j]-a[k+1])*(a[j]-a[k+1]); if(f[flag][j]==-1|| f[flag][j]>f[flag^1][k]+tmp) {
f[flag][j] = f[flag^1][k] + tmp; s[flag][j] = k; } } } flag ^=1; } } int main() {
int t, i, Case =0; scanf("%d", &t); while(t--) {
scanf("%d%d", &n, &m); if(m > n) m = n; for(i =1; i <= n; i++) scanf("%lld", a+i); sort(a+1, a+n+1); dp(); printf("Case %d: %lld\n", ++Case, f[flag^1][n]); } return0; }

 

hdu3516:

这个题看起来很像石子合并最小得分,但是却和石子合并不同,石子合并的w[i][j]值是恒定的,而这个题的w[i][j]却随决策变量k的不同而不同。所以这个题不能用贪心,需要用dp。

dp方程:f[i][j] = f[i][k]+f[k+1][j] + w[i][j]

但是这里有个问题,w[i][j]的取值和决策k是有关系的,不能直接看出是否满足四边形不等式。

可以这么想:如果我们固定k,由于w[i][j] = p[k+1].x-p[i].x+p[k].y-p[j].y,所以固定j时,w[i][j+1]-w[i][j] = (p[k+1].x-p[i].x+p[k].y-p[j].y) - (p[k+1].x-p[i].x+p[k].y-p[j+1].y) = (-p[j].y)-(-p[j+1].y) = p[j+1].y - p[j].y < 0 == c(因为题意说明:p[j+1].y严格小于p[j].y)。所以对于所有决策k,w[i][j+1]-w[i][j]随i增加始终是个常数,当然也可以说成是非递增或这说是不严格递减,所以可以认为w[i][j]是凸的;区间包含关系显而易见。所以综上,w[i][j]对于所有决策k均满足四边形不等式,所以f[i][j]也满足四边形不等式,所以可以到处决策单调性:s[i][j-1]<=s[i][j]<=s[i+1][j]。

View Code
#include 
#include
#include
#include
usingnamespace std; constint N =1010; struct node {
int x, y; }p[N]; int n, f[N][N], s[N][N]; bool cmp(const node &a, const node &b) {
return a.x < b.x; } void dp() {
int i, l, j, k, tmp; for(i =1; i <= n; i++) {
f[i][i] =0; s[i][i] = i; } for(l =1; l < n; l++) for(i =1; i+l <= n; i++) {
j = i+l; f[i][j] =-1; for(k = s[i][j-1]; k <= min(s[i+1][j], j-1); k++) {
tmp = p[k+1].x-p[i].x+p[k].y-p[j].y; if(f[i][j]==-1|| f[i][j]>f[i][k]+f[k+1][j]+tmp) {
s[i][j] = k; f[i][j] = f[i][k] + f[k+1][j] + tmp; } } } } int main() {
int i; while(scanf("%d", &n) != EOF) {
for(i =1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y); sort(p+1, p+n+1, cmp); dp(); printf("%d\n", f[1][n]); } return0; }

 

pku1160:

跟前该帖子描述的前两个dp是差不多的,这个题目我觉得比较巧的地方是预处理。

res[i][j] = res[i][j-1]+a[j]-a[mid]

为什么可以这么递推呢?因为区间(i,j)和区间(i,j+1)可以使用同一个中位数。因为这两个里面必然有一个区间长度为偶数,对于偶数长度来说,在该题中中间两个值都可以作为中位数,加入某一边扩展一点变成奇数长度的话,则取相应的靠近那一边的中间那个数当中位数就行啦。

View Code
#include 
#include
#include
#include
usingnamespace std; constint N =310; typedef longlong llg; int n, m, a[N], s[36][N], f[36][N], res[N][N]; void dp() {
int i, j, k, a, b, tmp; memset(f, -1, sizeof(f)); f[1][0] =0; for(i =1; i <= n; i++) {
f[1][i] = res[1][i]; s[1][i] =0; } for(i =2; i <= m; i++) {
s[i][n+1] = n; for(j = n; j >= i; j--) {
a = s[i-1][j]; b = s[i][j+1]; for(k = a; k <= min(b,j-1); k++) {
tmp = res[k+1][j]; if(f[i][j]==-1|| f[i][j]>f[i-1][k] + tmp) {
s[i][j] = k; f[i][j] = f[i-1][k] + tmp; } } } } } int main() {
int i, j, mid; scanf("%d%d", &n, &m); if(m > n) m = n; for(i =1; i <= n; i++) scanf("%d", a+i); a[0] =0; sort(a, a+n+1); for(i =1; i <= n; i++) {
res[i][i] =0; for(j = i+1; j <= n; j++) {
mid = (i+j) >>1; res[i][j] = res[i][j-1] + a[j] - a[mid]; } } dp(); printf("%d\n", f[m][n]); return0; }

 

转载于:https://www.cnblogs.com/zxndgv/archive/2011/08/02/2125274.html

你可能感兴趣的文章
18.综合应用判断素数
查看>>
mysql报错汇总
查看>>
洛谷P1162
查看>>
IE8,IE9,IE10,FireFox 的CSS HACK
查看>>
sql两张表对应同步数据(有数据做update没有数据没有数据做insert)
查看>>
(三)——Servlet
查看>>
java构造函数
查看>>
Oracle数据库基本操作(一) —— Oracle数据库体系结构介绍、DDL、DCL、DML
查看>>
[Poj1185][Noi2001]炮兵阵地(状压dp)
查看>>
胖子哥的大数据之路(五)- 数据资源-垄断的壁垒
查看>>
django简介
查看>>
继承与多态
查看>>
图片压缩工具之grunt-contrib-imagemin
查看>>
自定义 Core Data 迁移
查看>>
tcl的第二个脚本
查看>>
SDUT 1269 走迷宫(BFS)
查看>>
POJ1269(直线之间的关系)
查看>>
[C++面试]单例模式-设计模式
查看>>
Spring.Net学习笔记(5)-集合注入
查看>>
[zz]EI/SCI 检索信息
查看>>