• kuangbin带我飞(基础dp)


    001.Max Sum Plus Plus


    题目链接 https://vjudge.net/problem/HDU-1024

    (dp[i][j])为前j个数将其j为其中一组,分为i组每组相加的和。

    这状态转移方程为(:dp[i][j]=max(dp[i][j-1]+a[i],max(dp[i-1][k])+a[i]))

    其中(k<=j-1),

    复杂度为(O(mn^{2})),显然算法超时。

    我们可以对(max(dp[i-1][k]))把这层优化掉,即用Max[j]来前j个数分成i组最大的和。

    最后状态转移方程为(:d[j]=max(dp[j-1]+a[i]+Max[j-1]+a[i]))

    代码如下(:)

    #include <iostream>
    using namespace std;
    typedef long long ll;
    int m,n;
    ll a[1000007],dp[1000007],Mx[1000007],Max[1000007];
    int main(){
        while(scanf("%d %d",&m,&n)!=EOF){
            for(int i=1;i<=n;i++){
                scanf("%lld",&a[i]);
            }
            for(int i=0;i<=n;i++){
                dp[i]=Max[i]=0;
                Mx[i]=-1e18;
            }
            for(int i=1;i<=m;i++){
                for(int j=i;j<=n;j++){
                    dp[j]=max(dp[j-1]+a[j],Max[j-1]+a[j]);
                    Mx[j]=max(dp[j],Mx[j-1]);
                }
                for(int j=i;j<=n;j++){
                    Max[j]=Mx[j];
                    Mx[j]=-1e18;
                }
            }
            printf("%lld
    ",Max[n]);
        }
    }
    

    002.MonkeyandBanana


    题目链接:https://vjudge.net/problem/HDU-1069#author=prayerhgq

    解法一:(dfs+记忆化)

    枚举每个箱子的六种放法能否放在之前的箱子上,即这个箱子被之前的箱子长宽都小,直到不能放箱子即返回高度。

    直接枚举超时,加上记忆化搜索即可。

    #include <iostream>
    #include <cstring>
    using namespace std;
    struct node{
        int l,w,h;
    }pre[100];
    int n,tot,dp[100][10];
    int dfs(int p,int s){
        if(dp[p][s]!=-1) return dp[p][s];
        int ans=0,l,w;
        if(s==1){
            l=pre[p].l,w=pre[p].w;
        }else if(s==2){
            l=pre[p].w,w=pre[p].l;
        }else if(s==3){
            l=pre[p].l,w=pre[p].h;
        }else if(s==4){
            l=pre[p].h,w=pre[p].l;
        }else if(s==5){
            l=pre[p].w,w=pre[p].h;
        }else if(s==6){
            l=pre[p].h,w=pre[p].w;
        }
        for(int i=1;i<=n;i++){
            if(pre[i].l<l&&pre[i].w<w){
                ans=max(ans,dfs(i,1)+pre[i].h);
            }
            if(pre[i].w<l&&pre[i].l<w){
                ans=max(ans,dfs(i,2)+pre[i].h);
            }
            if(pre[i].l<l&&pre[i].h<w){
                ans=max(ans,dfs(i,3)+pre[i].w);
            }
            if(pre[i].h<l&&pre[i].l<w){
                ans=max(ans,dfs(i,4)+pre[i].w);
            }
            if(pre[i].w<l&&pre[i].h<w){
                ans=max(ans,dfs(i,5)+pre[i].l);
            }
            if(pre[i].h<l&&pre[i].w<w){
                ans=max(ans,dfs(i,6)+pre[i].l);
            }
        }
        return dp[p][s]=ans;
    }
    int main(){
        while(scanf("%d",&n)&&n!=0){
            int ml=0;
            for(int i=1;i<=n;i++){
                scanf("%d %d %d",&pre[i].l,&pre[i].w,&pre[i].h);
                ml=max(ml,max(pre[i].h,max(pre[i].w,pre[i].l)));
            }
            memset(dp,-1,sizeof(dp));
            int res=0;
            for(int i=1;i<=n;i++){
                res=max(res,dfs(i,1)+pre[i].h);
                res=max(res,dfs(i,2)+pre[i].h);
                res=max(res,dfs(i,3)+pre[i].w);
                res=max(res,dfs(i,4)+pre[i].w);
                res=max(res,dfs(i,5)+pre[i].l);
                res=max(res,dfs(i,6)+pre[i].l);
    
            }
            printf("Case %d: maximum height = %d
    ",++tot,res);
        }  
    }
    

    解法二 dp

    由题意易得,每个箱子的六个形态最多用一次,这就相当于有(6*n)个箱子

    前个箱子的长宽要大于后个箱子,那么就先对每个箱子按长度由大到小排序

    这样我们能推出状态转移方程为(:d[i]=max(d[i],d[j]+pre[i].h))

    其中d[i]为选前i个箱子堆成的高度,其中i箱子在最上面,(pre[i].h)为第i个箱子的高度

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int n,cnt,dp[1000];
    struct node{
        int l,w,h;
    }pre[1000];
    bool cmp(node x,node y){
        return x.l>y.l;
    }
    int main(){
        while(scanf("%d",&n)&&n!=0){
            int tot=0;   
            for(int i=1;i<=n;i++){
                int l,w,h;
                scanf("%d %d %d",&l,&w,&h);
                pre[++tot].l=l,pre[tot].w=w,pre[tot].h=h;
                pre[++tot].l=w,pre[tot].w=l,pre[tot].h=h;
                pre[++tot].l=l,pre[tot].w=h,pre[tot].h=w;
                pre[++tot].l=h,pre[tot].w=l,pre[tot].h=w;
                pre[++tot].l=h,pre[tot].w=w,pre[tot].h=l;
                pre[++tot].l=w,pre[tot].w=h,pre[tot].h=l;
            }
            memset(dp,0,sizeof(dp));
            sort(pre+1,pre+1+tot,cmp);
            int ans=0;
            for(int i=1;i<=tot;i++){
                dp[i]=pre[i].h;
                for(int j=1;j<=i-1;j++){
                    if(pre[j].w>pre[i].w&&pre[j].l>pre[i].l)
                        dp[i]=max(dp[i],dp[j]+pre[i].h);
                }
                ans=max(ans,dp[i]);
            }
            printf("Case %d: maximum height = %d
    ",++cnt,ans);
        }
    }
    
    

    003.Super Jumping! Jumping! Jumping!


    LIS变形,状态转移方程 dp[i]为前i个数以第i个数结尾的上升子序列的和。

    状态转移方程为:(dp[i]=max(dp[i],dp[j]+a[i]))

    最后取(ans=max(dp[i]))

    #include <iostream>
    using namespace std;
    typedef long long ll;
    int n;
    ll a[1007],dp[1007];
    int main(){
        while(scanf("%d",&n)&&n!=0){
            for(int i=1;i<=n;i++){
                scanf("%lld",&a[i]);
            }
            ll ans=0;
            for(int i=1;i<=n;i++){
                dp[i]=a[i];
                for(int j=1;j<=i-1;j++){
                    if(a[j]<a[i]){
                        dp[i]=max(dp[i],dp[j]+a[i]);
                    }
                }
                ans=max(ans,dp[i]);
            }
            printf("%lld
    ",ans); 
        }
    }
    

    004.Piggy-Bank


    完全背包问题变形。需注意dp[i]初始化为(1e9),(dp[0]=0)

    状态转移方程为:(dp[j]=max(dp[j],dp[j-v[i]]+w[i]))

    其中(jge v[i])(dp[j-v[i]] e1e9)

    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    int n,a[1000007],dp[1000007],cnt;
    int main(){
        while(scanf("%d",&n)!=EOF){
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                dp[i]=0;
            }
            cnt=0;
            for(int i=1;i<=n;i++){
                int inx=lower_bound(dp,dp+cnt,a[i])-dp;
                dp[inx]=a[i];
                if(inx==cnt){
                    cnt++;
                }
            }
            printf("%d
    ",cnt);
        }
    }
    

    005.免费馅饼


    暴搜,每秒能有三种状态,在当期坐标(p),在(p-1)坐标,在(p+1)坐标。

    暴搜超时,最后加上记忆化即可。

    #include <iostream>
    #include <cstring>
    using namespace std;
    int n,mx,dp[20][100007],tem[20][100007];
    int dfs(int p,int tim){
        if(tim>mx){
            return 0;
        }
        if(dp[p][tim]!=-1) return dp[p][tim];
        int ans=0;
        if(p+1<=10) ans=max(ans,dfs(p+1,tim+1)+tem[p][tim]);
        if(p-1>=0) ans=max(ans,dfs(p-1,tim+1)+tem[p][tim]);
        ans=max(ans,dfs(p,tim+1)+tem[p][tim]);
        return dp[p][tim]=ans;
    }
    int main(){
        while(scanf("%d",&n)&&n!=0){
            mx=0;
            memset(tem,0,sizeof(tem));
            memset(dp,-1,sizeof(dp));
            for(int i=1;i<=n;i++){
                int t,x;
                scanf("%d %d",&x,&t);
                if(t>mx) mx=t;
                tem[x][t]++;
            }
            printf("%d
    ",dfs(5,0));
        }
    }
    

    006.Tickets


    一维递推。

    状态转移方程(dp[i]=max(dp[i-1]+a[i],dp[i-2]+b[i]))

    或者记忆化搜索

    #include <iostream>
    #include <cstring>
    using namespace std;
    int n,k,s[2007],d[2007],dp[2007];
    int dfs(int p){
        if(p>k){
            return 0;
        }
        if(dp[p]!=-1) return dp[p];
        int ans=1e9;
        ans=min(ans,dfs(p+1)+s[p]);
        if(p!=k) ans=min(ans,dfs(p+2)+d[p]);
        return dp[p]=ans;
    }
    int main(){
        scanf("%d",&n);
        while(n--){
            scanf("%d",&k);
            memset(dp,-1,sizeof(dp));
            for(int i=1;i<=k;i++){
                scanf("%d",&s[i]);
            }
            for(int i=1;i<=k-1;i++){
                scanf("%d",&d[i]);
            }
            int t=8*3600+dfs(1);
            int go=t/(3600*12);
            int h=(t/3600)%12;
            int m=(t/60)%60;
            int s=t%60;
            if(go%2==0){
                printf("%02d:%02d:%02d am
    ",h,m,s);
            }else{
                printf("%02d:%02d:%02d pm
    ",h,m,s);
            }
        }
    }
    

    007.最少拦截系统


    题意是要找最少条非严格下降子序列,可转换成找一条严格上升子序列。

    复杂度(o(n^2))的就不赘述了。

    下面说说(o(nlogn))的做法。

    首先定义一下dp[i]为长度为i的上升子序列第i个数的值

    如果想要一个上升子序列尽可能长,那么dp[i]的数尽可能小,那么他后面能接的数也会尽可能的多。

    例如一个序列:389 207 155 300 299 170 158 65。

    我们要找它的最长上升子序列。首先在dp数组上找大于或等于389的数,将他插入dp数组。

    (dp={389})

    找大于或等于207的数,我们发现207能代替389。

    (dp={207})

    找大于或等于155的数。

    (dp={155})

    找大于或等于300的数。

    (dp={155,300})

    找大于或等于299的数。

    (dp={155,299})

    找大于或等于170的数。

    (dp={155,170})

    找大于或等于158的数。

    (dp={155,158})

    找大于或等于207的数

    (dp={155,207})

    所以最长上升子序列的长度为2。

    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    int n,a[1000007],dp[1000007],cnt;
    int main(){
        while(scanf("%d",&n)!=EOF){
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                dp[i]=0;
            }
            cnt=0;
            for(int i=1;i<=n;i++){
                int inx=lower_bound(dp,dp+cnt,a[i])-dp;
                dp[inx]=a[i];
                if(inx==cnt){
                    cnt++;
                }
            }
            printf("%d
    ",cnt);
        }
    }
    

    008.Jury Compromise


    (dp[i][j][k])为前i个人选j个,(k=a[i]-b[i])(dp[i][j][k])的值为前i个(a[i])(b[i])的和。

    有选或不选两个状态转移。

    选的话(dp[i][j][k]=dp[i-1][j-1][k+(a[i]-b[i])])((k+(a[i]-b[i])可能为负数,加个(base)偏移量))。

    不选(dp[i][j][k]=dp[i-1][j][k])

    当k相同时选前i个a[i]和b[i]最大的

    那么状态转移方程为(:dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-1][k+(a[i]-b[i])]))

    (dp[i-1][j][k]!=dp[i][j][k]),代表选择了第i个人,统计(suma)(sumb),和第(i)个人

    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int base=400;
    int n,m,tot,a[207],b[207],x[27][207],y[27][207];
    int dp[207][27][807];
    vector <int> ans;
    int main(){
        while(scanf("%d %d",&n,&m)!=EOF){
            if(n==0&&m==0) break;
            for(int i=1;i<=n;i++){
                scanf("%d %d",&a[i],&b[i]);
            }
            memset(dp,-inf,sizeof(dp));
            ans.clear();
            dp[0][0][base]=0;
            for(int i=1;i<=n;i++){
                for(int j=0;j<=m;j++){
                    for(int k=0;k<=800;k++){
                        dp[i][j][k]=dp[i-1][j][k];
                        int t=k+(a[i]-b[i]);
                        if(t<0||t>=800) continue;
    				    if(j<1) continue; 
                        if(k+a[i]-b[i]>=0)    
                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][t]+a[i]+b[i]);
                    }
                }
            }
            int v=0;
            while(dp[n][m][base+v]<0&&dp[n][m][base-v]<0) v++;
            if (dp[n][m][base-v]>dp[n][m][base+v]){
        		v=base-v;		
            }else{
                v=base+v; 
            }
            int i=n,j=m,suma=0,sumb=0;
            while(j){
                if(dp[i][j][v]==dp[i-1][j][v]){
                    i--;
                }else{
                    suma+=a[i];
                    sumb+=b[i];
                    ans.push_back(i);
                    v=v+(a[i]-b[i]);
                    i--,j--;
                }
            }
            sort(ans.begin(),ans.end());
            printf("Jury #%d 
    ",++tot);
            printf("Best jury has value %d for prosecution and value %d for defence: 
    ",suma,sumb);
            for(int i=0;i<ans.size();i++){
                printf("%d ",ans[i]);
            }
            printf("
    ");
        }
    }
    

    009.Common Subsequence


    最长公共子序列板子题。

    (dp[i][j])(s)串的前(i)的子序列和(t)串前(j)的子序列的最长公共子序列。

    (s[i]==t[j])时。

    (dp[i][j]=dp[i-1][j-1]+1)

    (s[i]!=t[j])时。

    (dp[i][j]=max(dp[i-1][j],dp[i][j-1]))

    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int n,m,dp[1000][1000];
    char s1[1000],s2[1000];
    int main(){
        while(cin>>(s1+1)>>(s2+1)){
            n=strlen(s1+1);
            m=strlen(s2+1);
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    if(s1[i]==s2[j]){
                        dp[i][j]=dp[i-1][j-1]+1;
                    }else{
                        dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                    }
                }
            }
            printf("%d
    ",dp[n][m]);
        }
    }
    
    

    010.Help Jimmy


    离散+记忆化搜索。

    #include <iostream>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int T,N,X,Y,MAX,dp[1007][2007];
    struct node{
        int x,y,h;
    }pre[1007];
    vector <int> ho;
    bool cmp(node x,node y){
        return x.h>y.h;
    }
    int get(int x){
        return lower_bound(ho.begin(),ho.end(),x)-ho.begin();
    }
    int dfs(int pos,int s){
        int ans=1e9,ok1=1,ok2=1;
        //cout<<"pos="<<pos<<" s="<<s<<endl;
        if(pos==N){
            return 0;
        }
        if(dp[pos][s]!=-1) return dp[pos][s];
        for(int i=1;i<=N;i++){
            if(pre[i].h>=pre[pos].h) continue;
            int l=pre[pos].x,r=pre[pos].y;
            if(ok1==1&&l>=pre[i].x&&l<=pre[i].y){
                ok1=0;
                if(abs(pre[pos].h-pre[i].h)<=MAX)    
                    ans=min(ans,dfs(i,l)+abs(ho[s]-ho[l])+abs(pre[pos].h-pre[i].h));
            }
            if(ok2==1&&r>=pre[i].x&&r<=pre[i].y){
                ok2=0;
                if(abs(pre[pos].h-pre[i].h)<=MAX)
                    ans=min(ans,dfs(i,r)+abs(ho[r]-ho[s])+abs(pre[pos].h-pre[i].h));
            }
            if(ok1==0&&ok2==0) break;
        }
        return dp[pos][s]=ans;
    }
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d %d %d %d",&N,&X,&Y,&MAX);
            ho.clear();
            for(int i=1;i<=N;i++){
                scanf("%d %d %d",&pre[i].x,&pre[i].y,&pre[i].h);
                ho.push_back(pre[i].x);
                ho.push_back(pre[i].y);
            }
            pre[N+1].x=-20000,pre[N+1].y=20000,pre[N+1].h=0;
            ho.push_back(pre[N+1].x),ho.push_back(pre[N+1].y);
            ho.push_back(X);
            N=N+1;
            ho.erase(unique(ho.begin(),ho.end()),ho.end());
            sort(ho.begin(),ho.end());
            for(int i=1;i<=N;i++){
                pre[i].x=lower_bound(ho.begin(),ho.end(),pre[i].x)-ho.begin();
                pre[i].y=lower_bound(ho.begin(),ho.end(),pre[i].y)-ho.begin();
            }
            memset(dp,-1,sizeof(dp));
            sort(pre+1,pre+1+N,cmp);
            int res=0;
            for(int i=1;i<=N;i++){
                if(get(X)>=pre[i].x&&get(X)<=pre[i].y){    
                    res=dfs(i,get(X))+abs(Y-pre[i].h);
                    break;
                }
            }
            printf("%d
    ",res);
        }
    }
    

    011.Longest Ordered Subsequence


    最长上升子序列,没什么好讲的

    #include <iostream>
    using namespace std;
    int n,a[1007],d[1007];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int res=0;
        for(int i=1;i<=n;i++){
            d[i]=1;
            for(int j=1;j<=i-1;j++){
                if(a[j]<a[i])
                    d[i]=max(d[i],d[j]+1);
            }
            res=max(res,d[i]);
        }
        printf("%d
    ",res);
    }
    

    012.Treats for the Cows

    区间dp或者记忆化搜索

    解法一:

    #include <iostream>
    using namespace std;
    typedef long long ll;
    int n;
    ll a[2007],dp[2007][2007];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int len=n-2;len>=0;len--){
            
            for(int i=1;i+len<=n;i++){
                int j=i+len;
                ll res1=0,res2=0;
                if(j+1<=n) res1=dp[i][j+1]+(n-len-1)*a[j+1];
                if(i-1>=1) res2=dp[i-1][j]+(n-len-1)*a[i-1];
                dp[i][j]=max(res1,res2);
                //printf("dp[%d][%d]=%d ",i,j,dp[i][j]);
            }
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,dp[i][i]+n*a[i]);
        }
        printf("%lld
    ",ans);
    }
    

    解法二:

    #include <iostream>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    int n;
    ll a[2007],dp[2007][2007];
    ll dfs(int x,int y){
        if(y<x){
            return 0;
        }
        if(dp[x][y]!=-1) return dp[x][y];
        ll ans=0;
        ans=max(dfs(x,y-1)+(n-abs(y-x))*a[y],dfs(x+1,y)+(n-abs(y-x))*a[x]);
        return dp[x][y]=ans;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        memset(dp,-1,sizeof(dp));
        printf("%lld
    ",dfs(1,n));
    }
    

    013.FatMouse and Cheese

    二维最长上升子序列

    #include <iostream>
    #include <cstring>
    #define judge(x,y) x>=1&&x<=n&&y>=1&&y<=n
    using namespace std;
    int n,m,dp[107][107],a[107][107];
    int dx[]={1,-1,0,0};
    int dy[]={0,0,-1,1};
    int dfs(int x,int y){
        if(dp[x][y]!=-1) return dp[x][y];
        int ans=a[x][y];
        for(int i=0;i<4;i++){
            for(int j=1;j<=m;j++){
                int nx=x+j*dx[i],ny=y+j*dy[i];
                if(judge(nx,ny)&&a[x][y]<a[nx][ny]){
                    ans=max(ans,dfs(nx,ny)+a[x][y]);
                }
            }
        }
        return dp[x][y]=ans;
    }
    int main(){
        while(scanf("%d %d",&n,&m)!=EOF){
            if(n==-1&&m==-1) break;
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    scanf("%d",&a[i][j]);
                }
            }
            memset(dp,-1,sizeof(dp));
            printf("%d
    ",dfs(1,1));
        }
    }
    
  • 相关阅读:
    递归函数
    Java以缓冲字符流向文件写入内容(如果文件存在则删除,否则先创建后写入)
    Python将文本内容读取分词并绘制词云图
    查询数据库数据并传入servlet
    向数据库添加记录(若有则不添加)
    2月16日学习记录
    2月15日学习记录
    2月14日学习记录
    Echart学习
    JavaScript深入学习(六)Ajax技术
  • 原文地址:https://www.cnblogs.com/kksk/p/13904629.html
Copyright © 2020-2023  润新知