2011-0056

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

这道题的意思是告诉你每天的西瓜要多少钱,以及这个西瓜可以吃几天,然后问你怎么买,可以每天吃西瓜且花钱最少。
因为买了一个新的西瓜,之前的西瓜无论有没有吃完,都没用了,所以购买策略应该是从前往后进行。
我们用f[i]表示,前i天能够吃到西瓜,需要多少钱。记a[i],b[i]表示第i天的西瓜的价格和可以吃的天数,接下来我们来思考状态转移。
对于第i天,很明显我们需要确保i-1天吃到西瓜,但是我们也可以选择这么一个策略:前面的西瓜还没有吃完,我买了新西瓜,把旧的西瓜扔了。
因此,我们选取一个p=min(f[j])i-1<=j<=n,显然对于这样取出来的p的j,对于第i天是合法的,而且这也是让前i-1天吃到西瓜的最有方案。
然后我们让f[i+b[i]-1]=min(p+a[i],f[i+b[i]-1])
实际上,我们这么做是发现,如果f[i+1]<f[i],我们可以用f[i+1]来替换f[i],但是如果一个个直接替换,太浪费时间。因此我们不选择替换,而是在决策时利用这个规律取值。
为了快速取得min(f[j])i-1<=j<=n,我们可以用一个线段树维护。
需要注意的一点是,它购买的总金额会很大,需要用long long 存,一开始用一个longlong级别的数来存f[i]的初值

#include <cstdio>

struct tree{
    int l,r,m;
    long long min;
};
int n;
int p[50001],d[50001];
long long o;
tree a[150001];

inline long long min(long long x,long long y)
{
    if(x<=y) return x; else return y;
}

inline void gengx(int k)
{
    a[k].min=min(a[k*2].min,a[k*2+1].min);
    return;
}

void make(int k,int l,int r)
{
    a[k].l=l;a[k].r=r;
    a[k].m=(l+r)/2;
    long long o=n;
    a[k].min=o*100000;
    if(l==r) return;
    make(k*2,l,a[k].m);
    make(k*2+1,a[k].m+1,r);
    gengx(k);
}

long long find(int k,int x)
{
    if(a[k].l>=x) return a[k].min;
    if(a[k].m<x) return find(k*2+1,x);
    else return min(find(k*2,x),find(k*2+1,x));
}

void insert(int k,long long s,int x)
{
    if(a[k].l==a[k].r) 
    {
        a[k].min=min(a[k].min,s);
        return;
    }
    if(x<=a[k].m) insert(k*2,s,x);
    else insert(k*2+1,s,x);
    gengx(k);
    return;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&p[i]);
        for(int i=1;i<=n;i++) scanf("%d",&d[i]);
        make(1,1,n);
        for(int i=1;i<=n;i++)
        {
            if(i==1) o=0; else o=find(1,i-1);
            insert(1,o+p[i],i+d[i]-1);
        }
        printf("%lld\n",find(1,n));
    }
    return 0;
}
 by hzwlzrj

这道题的意思是告诉你每天的西瓜要多少钱,以及这个西瓜可以吃几天,然后问你怎么买,可以每天吃西瓜且花钱最少。

因为买了一个新的西瓜,之前的西瓜无论有没有吃完,都没用了,所以购买策略应该是从前往后进行。

我们用f[i]表示,前i天能够吃到西瓜,需要多少钱。记a[i],b[i]表示第i天的西瓜的价格和可以吃的天数,接下来我们来思考状态转移。

对于第i天,很明显我们需要确保i-1天吃到西瓜,但是我们也可以选择这么一个策略:前面的西瓜还没有吃完,我买了新西瓜,把旧的西瓜扔了。

因此,我们选取一个p=min(f[j])i-1<=j<=n,显然对于这样取出来的p的j,对于第i天是合法的,而且这也是让前i-1天吃到西瓜的最有方案。

然后我们让f[i+b[i]-1]=min(p+a[i],f[i+b[i]-1])

实际上,我们这么做是发现,如果f[i+1]

为了快速取得min(f[j])i-1<=j<=n,我们可以用一个线段树维护。

需要注意的一点是,它购买的总金额会很大,需要用long long 存,一开始用一个longlong级别的数来存f[i]的初值

#include

struct tree{

int l,r,m;

long long min;

};

int n;

int p[50001],d[50001];

long long o;

tree a[150001];

inline long long min(long long x,long long y)

{

if(x<=y) return x; else return y;

}

inline void gengx(int k)

{

a[k].min=min(a[k*2].min,a[k*2+1].min);

return;

}

void make(int k,int l,int r)

{

a[k].l=l;a[k].r=r;

a[k].m=(l+r)/2;

long long o=n;

a[k].min=o*100000;

if(l==r) return;

make(k*2,l,a[k].m);

make(k*2+1,a[k].m+1,r);

gengx(k);

}

long long find(int k,int x)

{

if(a[k].l>=x) return a[k].min;

if(a[k].m

else return min(find(k*2,x),find(k*2+1,x));

}

void insert(int k,long long s,int x)

{

if(a[k].l==a[k].r)

{

a[k].min=min(a[k].min,s);

return;

}

if(x<=a[k].m) insert(k*2,s,x);

else insert(k*2+1,s,x);

gengx(k);

return;

}

int main()

{

while(scanf("%d",&n)!=EOF)

{

for(int i=1;i<=n;i++) scanf("%d",&p[i]);

for(int i=1;i<=n;i++) scanf("%d",&d[i]);

make(1,1,n);

for(int i=1;i<=n;i++)

{

if(i==1) o=0; else o=find(1,i-1);

insert(1,o+p[i],i+d[i]-1);

}

printf("%lld\n",find(1,n));

}

return 0;

}

by hzwlzrj