数据结构dp题


hdu5542 The Battle of Chibi

题意:

给长度为n的数组a,和一个整数m
要求计算数组中长度为m的子序列数量,满足子序列严格递增
答案对1e9+7取模
数据范围:n,m<=1e3

思路:

因为n和m只有1e3,:
令d[i][j]表示弟以i结尾长度为j的上升子序列数量
容易想到转移方程为:d[i][j]=sigma(d[k][j-1]),其中1<=k<i,且a[k]<a[i]
枚举i,j是O(n^2),加上k则为O(n^3),需要优化
考虑到第三维是累加长度为j-1的所有d[k],a[k]<a[i]
可以用二维树状数组c[x][j]存储以值x为结尾的长度为j的上升子序列数量
这样第三维就能降到log(n),总复杂度O(n^2*log(n))

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
const int mod=1e9+7;
int d[maxm][maxm];
int c[maxm][maxm];
int xx[maxm];
int a[maxm];
int n,m;
int lowbit(int i){
    return i&-i;
}
void add(int x,int len,int t){
    while(x<maxm){
        c[x][len]+=t;
        c[x][len]%=mod;
        x+=lowbit(x);
    }
}
int ask(int x,int len){
    int ans=0;
    while(x){
        ans+=c[x][len];
        ans%=mod;
        x-=lowbit(x);
    }
    return ans;
}
signed main(){
    int T;
    cin>>T;
    int cas=1;
    while(T--){
        memset(c,0,sizeof c);
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            xx[i]=a[i];
        }
        sort(xx+1,xx+1+n);
        int num=unique(xx+1,xx+1+n)-xx-1;
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(xx+1,xx+1+num,a[i])-xx+3;
        }
        for(int i=1;i<=n;i++){
            d[i][1]=1;
            add(a[i],1,1);
            for(int j=2;j<=m&&j<=i;j++){
                d[i][j]=ask(a[i]-1,j-1);
                add(a[i],j,d[i][j]);
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            ans+=d[i][m];
            ans%=mod;
        }
        printf("Case #%d: ",cas++);
        cout<<ans<<endl;
    }
    return 0;
}

CodeForces833 B. The Bakery

题意:

给长度为n的数组a,和一个整数k
要求把数组分成连续的k段,每段的权值是该段中不同数的个数,
输出最大权值和。
数据范围:n<=35000,k<=min(n,50),1<=a(i)<=n

完整题意:
n个蛋糕k个盒子,要求把蛋糕分成来k段,每段连续,分别用盒子装,
每个盒子的价值是盒子内不同蛋糕的数量,要求计算最大价值

思路:

d[i][j]表示前i个盒子装下前j蛋糕的最大价值
col[i][j]表示区间[i,j]中不同数的个数
显然d[1][j]=col[1][j]

考虑在原来的基础上再切一刀,可推出转移方程为:
d[i][j]=max{d[i-1][k]+col[k+1][i]},其中k<j

因为要枚举i,j,k,因此复杂度是O(n^2*k)的
而n最大35000,这个复杂度显然是不满足要求的,想办法优化掉一个n

因为d[i][j]=max{d[i-1][k]+col[k+1][i]},max操作可以想到线段树
但是要先将d[i-1][k]+col[k+1][i]全部计算出来

建立一颗线段树,第k个位置为d[i-1][k-1]

考虑每个数有价值的区间:
每个数有价值的范围由前一个与他相同数的位置决定,例如:
a[3]=5,a[5]=5,则a[5]有价值的区间为[4,5],
通过记录前一个数的位置可以O(n)把每个数有价值的区间求出来

假如一个数j的有价值区间为[a,b],则对线段树的[a,b]区间加1贡献
这样之后线段树中的每个位置就是d[i-1][k]+col[k+1][i][1,j]中的max来更新d[i][j]

---
总结:
dp转移的过程中,一些数据需要直接计算
有时候可以拆成若干小数据分别计算贡献

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=4e4+5;
int laz[maxm<<2],t[maxm<<2];
int d[55][maxm];
int mark[maxm];
int pre[maxm];
int a[maxm];
int n,k;
void pushup(int node){
    t[node]=max(t[node*2],t[node*2+1]);
}
void pushdown(int node){
    if(laz[node]){
        laz[node*2]+=laz[node];
        laz[node*2+1]+=laz[node];
        t[node*2]+=laz[node];
        t[node*2+1]+=laz[node];
        laz[node]=0;
    }
}
void build(int l,int r,int node,int i){
    t[node]=laz[node]=0;
    if(l==r){
        t[node]=d[i-1][l-1];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,node*2,i);
    build(mid+1,r,node*2+1,i);
    pushup(node);
}
void update(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r){
        laz[node]++;
        t[node]++;
        return ;
    }
    pushdown(node);
    int mid=(l+r)/2;
    if(st<=mid)update(st,ed,l,mid,node*2);
    if(ed>mid)update(st,ed,mid+1,r,node*2+1);
    pushup(node);
}
int ask(int st,int ed,int l,int r,int node){
    if(st<=l&&ed>=r){
        return t[node];
    }
    pushdown(node);
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans=max(ans,ask(st,ed,l,mid,node*2));
    if(ed>mid)ans=max(ans,ask(st,ed,mid+1,r,node*2+1));
    return ans;
}
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        pre[i]=mark[a[i]]+1;
        mark[a[i]]=i;
    }
    for(int i=1;i<=k;i++){
        build(1,n,1,i);
        for(int j=1;j<=n;j++){
            update(pre[j],j,1,n,1);
            d[i][j]=ask(1,j,1,n,1);
        }
    }
    cout<<d[k][n]<<endl;
    return 0;
}

发布了445 篇原创文章 · 获赞 37 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/105064176
今日推荐