您的位置:首页 > 其它

dp优化--斜率

2012-08-25 21:33 316 查看
给出学习的连接:

http://www.notonlysuccess.com/index.php/dp_optimize/

hdu 2993:

#include "cstdio"
#include "iostream"
#include "algorithm"

using namespace std;

typedef pair<int, int> point;
typedef __int64 ll;

const int N = 111111;

point poi
;
int sum
;

ll cross(point a, point b, point c){
ll x0 = b.first - a.first,
y0 = b.second - a.second,
x1 = c.first - a.first,
y1 = c.second - a.second;

return x0*y1 - x1*y0;
}

int bsearch(int l, int r, point p){
while(l<r){
int mid = (l+r)>>1;
if(cross(poi[mid], poi[mid+1], p) < 0) { r = mid; }
else { l = mid+1; }
}
return l;
}

int GetInt(){
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
int num=0;
while(ch>='0'&&ch<='9'){
num=num*10+ch-'0';
ch=getchar();
}
return num;
}

int main()
{
int n, k, i, j, l, r;
double ans, tmp;
point curp, newp;

while(~scanf("%d%d", &n, &k))
{
sum[0] = 0;
for(i = 1; i <= n; ++i){
sum[i] = GetInt();
sum[i] += sum[i-1];
}

ans = 0;
l = 1;
r = 0;
for(i = k; i <= n; ++i){
newp.first = i-k;
newp.second = sum[i-k];
curp.first = i;
curp.second = sum[i];

while(r>l && cross(poi[r-1], poi[r], newp)<0) { --r; }
poi[++r] = newp;

for(j = l; j < r && cross(poi[j], poi[j+1], curp) >= 0; ++j) ;
l = j; // n

// l = bsearch(l, r, curp); // n
// l = bsearch(1, r, curp); // nlg(n)
tmp = (double)(poi[l].second-sum[i])/(poi[l].first - i);
if(tmp>ans) { ans = tmp; }
}
printf("%.2lf\n", ans);
}

return 0;
}


// poi可以不存

// 注意两种删点,最优值(左边),凹形函数(右边)

hdu4258:(http://serjudging.vanb.org/?p=359)

最原始的dp方程:dp[i]=min{dp[j-1]+(x[i]-x[j])^2+c} , dp[i]是i的最优值,j<=i,显然是n^2的,需要优化....

优化:假设k<j<i, 对于i来说,如果j点决策比k点好,即根据上面的方程j点算出来<k点,如下...

dp[j-1]+(x[i]-x[j])^2+c < dp[k-1]+(x[i]-x[k])^2+c...展开,化简...

dp[j-1]+x[j]^2-2*x[i]*x[j]<dp[k-1]+x[k]^2-2*x[i]*x[k]...移项...

dp[j-1]+x[j]^2-(dp[k-1]+x[k]^2)<x[i]*(2*x[j]-2*x[k])....这里将dp[j-1]+x[j]^2视为Y[j],2*x[j]视为X[j]...得到

Y[j]-Y[k]<x[i]*(X[j]-X[k])...因为X[j]-X[k]>0...可以除过去...得到...注意x和X是不同的....

(Y[j]-Y[k])/(X[j]-X[k])<x[i]....视左边的为g[j, k]...注意要保证j,k顺序,保证X[j]-X[k]>0

接下来, 关键的来了:如今从左到右,还是设k<j<i,若是g[i,j]<g[j,k],那么j点便永远不成能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?

我们假设g[i,j]<x[i],那么就是说i点要比j点优,打消j点。

若是g[i,j]>=x[i],那么j点此时是比i点要更优,然则同时g[j,k]>g[i,j]>x[i]。这申明还有k点会比j点更优,同样打消j点。

打消多余的点,这便是一种优化!

于是对于这题我们对于斜率优化做法可以总结如下:

1,用一个单调队列来保护解集。

2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时辰,我们保护队列的上凸性质,即若是g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点参加在该地位中。

3,求解时,从队头开端,若是已有元素a b c,当i点请求解时,若是g[b,a]<x[i],那么申明b点比a点更优,a点可以打消,于是a出队。最后dp[i]的决策就是最左边的那个点。

#include "cstdio"
#include "iostream"
using namespace std;

typedef __int64 ll;
typedef pair<ll, ll> pp;

const int N = 1111111;

ll po
, qu
, dp
;

ll gg(int i, int j){
ll yi = dp[i-1] + po[i]*po[i],
yj = dp[j-1] + po[j]*po[j],
xi = 2*po[i], xj=2*po[j];
return (yi-yj)/(xi-xj);
}

ll check(pp p1, pp p2){
return (p1.second*p2.first-p2.second*p1.first);
}

ll calc(int i, int j, int k){
return (dp[j-1]+(po[i]-po[j])*(po[i]-po[j]))-(dp[k-1]+(po[i]-po[k])*(po[i]-po[k]));
}

ll GetLL(){
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
ll num=0;
while(ch>='0'&&ch<='9'){
num=num*10+ch-'0';
ch=getchar();
}
return num;
}

int main(){
int i, n, c, l, r;

//	freopen("covered.in", "r", stdin);

while(scanf("%d%d", &n, &c) && n+c)
{
for(i=1; i<=n; ++i)
{
po[i] = GetLL(); // 外挂
// scanf("%I64d", &po[i]);
}

l=0; r=-1;
for(i=1; i<=n; ++i)
{
dp[i]=0;
while(l<r && gg(i, qu[r])<gg(qu[r], qu[r-1])) {
--r;
}
qu[++r]=i;

while(l<r && calc(i, qu[l+1], qu[l])<0) {
++l;
}
dp[i] = dp[qu[l]-1]+(po[i]-po[qu[l]])*(po[i]-po[qu[l]]) + c;

//	printf("%d %d %I64d\n", i, qu[l], dp[i]);
}
printf("%I64d\n", dp
);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: