数据结构优化dp

由于考试考得不太好 一个寒假没碰过笔也没写作业233能考好才怪。

当然 也不是坏到了极点。当然还可以坚持像以前一样来机房 写一些有意思的题目了!

早就想A那道题目了,看到qy dalao 去写了我想我也能写。

对于这道题目 贪心肯定是不行的吧,不知道从哪贪 30分。

裸的n^2 dp 那么就可以到手了。 f[i]表示 第i个磁带合法的最小代价

f[i]=min(f[i],f[j]+abs(a[j+1]-(i-j-1)));这样得到了30分 很容易的一个n^2 算法dp

然后瞄着 70分考虑优化,花费了我3个小时也只想到了 去绝对值。

设 k=i-i; f[i]=min{f[j]+a[j+1]+j-k}; 当且仅当 a[j+1]+j-k>=0

或者 f[i]=min(f[j]+k-a[j+1]-j); 当且仅当 a[j+1]+j-k<=0 

我们发现有两个决策集合 这个斜率优化? 没什么用

单调队列也不行啊 因为k不具单调性 四边形不等式?(去一边区间dp

只能是数据结构优化了,树状数组?线段树?亦或者 权值线段树?

感觉都不太行啊,观察线段树的话我开两个 然后以什么为下标呢 a[j+1]+j 

好像是可以很大很大的 但是可以离散复杂度稍微高一点且非常不好写。

qy dalao倒是交给我了一种方法,倒序dp发现很容易解决这个问题,且维护很好维护。

也很好写,当然我自己也是经过严密的思考(旷了早读,列了一堆式子

倒序dp:f[i]表示 第后i个磁带合法的最小代价  f[i]=min(f[i],abs(a[i]-j+1+i)+f[j])

设 k=a[i]+1+i f[i]=min{abs(k-j)+f[j]}那么这个问题如下:

分类讨论: 当 k-j>=0 时我们需要寻找 k~n 这个决策集合中最小的 f[j]-j ;

                   当k-j<=0 时我们需要寻找 1~k  这个决策集合中最小的 f[j]+j;

怎么寻找呢 开 multiset? 不太行 因为我们还要维护下标(不用离散 好开心好开心。

开两个线段树吧,维护一个区间最小值即可。此题结束。

另外值得一提的是这道题卡常 把我正确率都卡下去了QAQ

真的是丧心病狂的卡常 我最后把常数都卡到最小了 还是T 1一点。

无奈我使出了 把两个线段树当成一个线段树用减小近乎一倍的常数A掉了这道题呢!

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cctype>
#include<utility>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<ctime>
#include<set>
#include<bitset>
#include<map>
#include<cmath>
#include<vector>
#include<cstdlib>
#define INF 2147483646
#define INF1 168430090
#define ll long long
#define l(x) s[x].l
#define r(x) s[x].r
#define v(x) s[x].v
#define R register
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
//考虑dp f[i]表示 第i个磁带合法的最小代价 
//显然 f[i]=min(f[i],abs(a[i]-j+1+i)+f[j])
//设 k=a[i]+1+i f[i]=min{abs(k-j)+f[j]}
const int MAXN=1000002;
int n;
int f[MAXN],a[MAXN];
inline int min(int x,int y)
{
    return x>y?y:x;
}
struct wy
{
    int l,r;
    int v;
}t[MAXN<<2];//k>=j 的决策区间
struct wy1//k<=j 的决策区间
{
    int l,r;
    int v;
}s[MAXN<<2];
inline void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;
    l(p)=l;r(p)=r;
    if(l==r)
    {
        if(l==n)t[p].v=-n,v(p)=n;
        else t[p].v=INF,v(p)=INF;
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p].v=min(t[p<<1].v,t[p<<1|1].v);
    v(p)=min(v(p<<1),v(p<<1|1));
}
inline void change(int p,int l,int r,int d,int dd)
{
    if(l<=t[p].l&&r>=t[p].r){t[p].v=d;v(p)=dd;return;}
    int mid=(t[p].l+t[p].r)>>1;
    if(l<=mid)change(p<<1,l,r,d,dd);
    if(r>mid)change(p<<1|1,l,r,d,dd);
    t[p].v=min(t[p<<1].v,t[p<<1|1].v);
    v(p)=min(v(p<<1),v(p<<1|1));
    return;
}
inline int ask(int p,int l,int r)
{
    if(l<=t[p].l&&r>=t[p].r)return t[p].v;
    int mid=(t[p].l+t[p].r)>>1;
    int cnt=INF;
    if(l<=mid)cnt=min(cnt,ask(p<<1,l,r));
    if(r>mid)cnt=min(cnt,ask(p<<1|1,l,r));
    return cnt;
}
inline int ask1(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p))return v(p);
    int mid=(l(p)+r(p))>>1;
    int cnt=INF;
    if(l<=mid)cnt=min(cnt,ask1(p<<1,l,r));
    if(r>mid)cnt=min(cnt,ask1(p<<1|1,l,r));
    return cnt;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(R int i=1;i<=n;++i)a[i]=read(),f[i]=INF;;
    n++;build(1,1,n);
    for(R int i=n-1;i>=1;--i)
    {
        R int k=a[i]+i+1;
        f[i]=min(f[i],ask(1,1,k>n?n:k)+k);
        if(k<=n)f[i]=min(f[i],ask1(1,k,n)-k);
        change(1,i,i,f[i]-i,f[i]+i);
    }
    put(f[1]);
    return 0;
}
View Code

时不可兮骤得,聊逍遥兮容与!

原文地址:https://www.cnblogs.com/chdy/p/10455210.html