• kuangbin专题十二 基础DP1【从入门到熟练】【10题】


    HDU 1024 Max Sum Plus Plus

    感觉这题是这个系列里难度最高的题之一?

    dp[i][j]代表前j个数找i个区间和,且第j个数在被最后一个区间选中的情况下,的最大和

    接着转移是dp[i][j] = max( dp[i][j-1] , max(dp[i-1][k])  ) + a[j], k从i-1到j-1

    那么再靠前缀优化+滚动数组就可以ac了

    #include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e6 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int a[maxn];
    int dp[maxn][2],pre[maxn][2];//pre[i]是从合法位置到i的dp最大值
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int m,n;
        while( scanf("%d%d",&m,&n)!=EOF ){
            memset(dp,0,sizeof(dp));
            memset(pre,0,sizeof(pre));
            for(int i=1;i<=n;i++) scanf("%d",a+i);
            //前i个数找j个区间的最大和
            
            int last=0,now=1;//滚动数组
            //初始状态就是i=0的时候,都是dp[0][j]=0,那pre自然也是0
    
            for(int i=1;i<=m;i++){
                for(int j=1;j<=n;j++){
                    if(j<i) continue;
                    if(j==i) { dp[j-1][now]=-inf;  pre[j-1][now]=-inf; }//该子问题非法
    
                    dp[j][now] = max(dp[j-1][now],pre[j-1][last])+a[j];
                    pre[j][now] = max( pre[j-1][now],dp[j][now] );
                }
                last = !last;
                now = !now;
            }
            cout<<pre[n][last]<<endl;
    
        }
    
        
        return 0;
    }
    View Code

    HDU 1069 Monkey and Banana

    把一个block拆成6个block,然后排序,再求最长下降子序列就行了

    #include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e6 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int dp[200];//dp[i]为以第i个cube为结尾的最长下降子序列
    struct node{
        int l,w,h;
        node(int l1=0,int w1=0,int h1=0): l(l1),w(w1),h(h1) {}
    }cubes[200];
    
    bool cmp(node n1,node n2){
        if( n1.l==n2.l ) return n1.w>n2.w;
        return n1.l>n2.l;
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int n,tc=0;
        while( scanf("%d",&n) ){
            if(n==0) break;
    
            for(int i=0;i<n;i++){
                int l,w,h; scanf("%d%d%d",&l,&w,&h);
                cubes[ i*6+1 ] = node(l,w,h);
                cubes[ i*6+2 ] = node(w,l,h);
                cubes[ i*6+3 ] = node(l,h,w);
                cubes[ i*6+4 ] = node(h,l,w);
                cubes[ i*6+5 ] = node(w,h,l);
                cubes[ i*6+6 ] = node(h,w,l); 
            }
            sort(cubes+1,cubes+6*n+1,cmp);
            cubes[0].l = cubes[0].w = cubes[0].h = inf;
            //最长递减子序列
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=6*n;i++){//以i为结尾
                for(int j=0;j<i;j++){//枚举倒数第二个数
                    if( cubes[j].l>cubes[i].l && cubes[j].w>cubes[i].w ) dp[i] = max( dp[i],dp[j]+cubes[i].h );
                }
            }
            int ans = -inf;
            for(int i=1;i<=6*n;i++) ans=max(ans,dp[i]);
            printf("Case %d: maximum height = %d
    ",++tc,ans); 
        }
        
        return 0;
    }
    View Code

    HDU 1260 Tickets

    加个第二维0,1代表当前人是跟上一个一起买的还是单独买的,那就能转移了。

    dp[i][0]代表前i个人,在第i个人单独买票的情况下,总最小时间 = min( dp[i-1][0],dp[i-1][1] ) + a[i]

    dp[i][1]代表前i个人,在第i个人跟上一人一起买票的情况下,总最小时间= min( dp[i-2][0],dp[i-2][1] ) + b[i]

    #include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 2e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int dp[maxn][2];
    int a[maxn],d[maxn];
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int t; cin>>t;
        while(t--){
            int n; cin>>n;
            for(int i=1;i<=n;i++) cin>>a[i];
            for(int i=2;i<=n;i++) cin>>d[i];//i与i-1一起买票花的时间
            for(int i=1;i<=n;i++) dp[i][1] = dp[i][0] = inf;
    
            dp[1][0]=a[1]; dp[1][1]=inf;
            dp[0][0]=0; dp[0][1]=inf;
            for(int i=2;i<=n;i++){
                dp[i][0] = min( dp[i-1][0],dp[i-1][1] ) + a[i];
                dp[i][1] = min( dp[i-2][0],dp[i-2][1] ) + d[i];
            }
            
            int time = min( dp[n][0],dp[n][1] );
            //3600秒是1h
            int h=8+time/3600; time%=3600;
            //60秒是1分钟
            int m=time/60; time%=60;
            //08:00:08 am
            if( h<10 ) cout<<"0"<<h<<":";
            else cout<<h<<":";
            if( m<10 ) cout<<"0"<<m<<":";
            else cout<<m<<":";
            if(time<10) cout<<"0"<<time<<" ";
            else cout<<time<<" ";
    
            if( h>=12 ) cout<<"pm"<<endl;
            else cout<<"am"<<endl;
        }
        
        
        return 0;
    }
    View Code

    HDU 1257 最少拦截系统

    当作模拟做了,如果没有系统能打到那就开新的,如果能打到,那就拿能打的到的最低的打。易证整个导弹系统的拦截高度是个递增序列

    #include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e5 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int a[maxn],limit[maxn],cnt;//limit[i]是第i个拦截系统的最大高度
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int n;
        while( scanf("%d",&n)!=EOF ){
            for(int i=1;i<=n;i++) scanf("%d",a+i);
            limit[1] = a[1]; //第一个拦截系统打第一个导弹
            cnt=1;
            for(int i=2;i<=n;i++){
                bool flag=false;//目前没有系统能击落i导弹
                int pick;
                for(int j=1;j<=cnt;j++){
                    if( limit[j]>=a[i] ) { flag=true; pick=j; break; }
                }
                if( flag ) limit[pick]=a[i];
                else limit[++cnt]=a[i];
            }
            printf("%d
    ",cnt);
    
        }
    
        
        
        return 0;
    }
    View Code

    POJ 1015 Jury Compromise

    三维dp

    dp[i][j][k]是前i个人选j个人出来且辩控差为k情况下的辩控和最大值,那么转移方程是,由于k可能被减成负的,所以要加个足够大的常数400

    dp[i][j][k] = max( dp[i-1][j][k] , dp[i-1][j-1][ k-(d[i]-p[i]) ] + d[i] + p[i] )

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 2e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int dp[210][25][810],pre[210][25][810];
    int d[210],p[210];
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int n,m,tc=0;
        while( scanf("%d%d",&n,&m)!=EOF ){
            if(n==0 && m==0) break;
            for(int i=1;i<=n;i++) scanf("%d%d",p+i,d+i);
    
            memset(dp,-1,sizeof(dp));
            dp[0][0][400]=0;
    
            for(int i=1;i<=n;i++){
                for(int k=0;k<=800;k++) dp[i][0][k]=-1;//-1代表问题根本不合法
                dp[i][0][400]=0;
    
                for(int j=1;j<=min(i,m);j++){
                    for(int k=0;k<=800;k++){//枚举当前的辩控差
                        int cha = p[i]-d[i];//第i个人带来的辩控差
                        if( k-cha<0 || k-cha>800 ) continue;
                        if( dp[i-1][j][k]==-1 && dp[i-1][j-1][k-cha]!=-1 ) { dp[i][j][k] = dp[i-1][j-1][k-cha]+d[i]+p[i]; pre[i][j][k]=1; }
                        else if( dp[i-1][j][k]!=-1 && dp[i-1][j-1][k-cha]==-1 ) { dp[i][j][k] = dp[i-1][j][k]; pre[i][j][k]=0; }
                        else if( dp[i-1][j][k]==-1 && dp[i-1][j-1][k-cha]==-1 ) continue;
                        else{
                           if( dp[i-1][j][k]>dp[i-1][j-1][k-cha]+d[i]+p[i] ) { dp[i][j][k] = dp[i-1][j][k]; pre[i][j][k]=0; }
                           else { dp[i][j][k] = dp[i-1][j-1][k-cha]+d[i]+p[i]; pre[i][j][k]=1; } 
                        }
    
                      //  cout<<i<<" "<<j<<" "<<k<<" "<<dp[i][j][k]<<endl;
                    }
                }
    
            }
    
            int cnt1=0,cnt2=0;
            int n1=n,m1=m,k1=-1;
    
    
    
            if(dp[n][m][400]!=-1) k1=400;
            else{
                for(int i=1;i<=400;i++){
                    if( dp[n][m][400+i]!=-1 ) k1=400+i;
                    if( dp[n][m][400-i]!=-1 ){   
                        if( k1==-1) k1=400-i;
                        else if( dp[n][m][400-i]>dp[n][m][k1] ) k1=400-i;
                    }
                    if(k1!=-1) break;
                }
            }
           
            vector<int> ans;
            while(n1){
                //cout<<n1<<" "<<m1<<" "<<k1<<" "<<pre[n1][m1][k1]<<" "<<p[n1]<<" "<<d[n1]<<endl;
                if( pre[n1][m1][k1] ) { ans.push_back(n1); cnt1+=p[n1]; cnt2+=d[n1]; k1-=(p[n1]-d[n1]); n1--; m1--;  }
                else n1--;
            }
    
            if(tc) printf("
    ");
            printf("Jury #%d
    ",++tc);
            printf("Best jury has value %d for prosecution and value %d for defence: 
    ",cnt1,cnt2);
            for(int i=ans.size()-1;i>=0;i--) printf(" %d",ans[i]);
        }
        
    
        return 0;
    }
    View Code

    POJ 1661 Help Jimmy

    打个岔,这题可以当最短路做,但并没有看出来

    dp[i][0]是从第i个平台的左边往下掉到地面的最小时间 = min( dp[k][0] + 掉落时间 + 从摔落点跑到k平台左端点时间 , dp[k][1] + 掉落时间 + 从摔落点跑到k平台右端点时间 ) k是掉下去以后掉到的那个平台

    dp[i][1]就略了

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int dp[maxn][2];
    struct node{
        int l,r,h;
        node(int l1=0,int r1=0,int h1=0): l(l1),r(r1),h(h1) {}
    }plats[maxn];
    bool cmp(node n1,node n2){
        if( n1.h==n2.h ) return n1.l<n2.l;
        return n1.h<n2.h;
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int t; cin>>t;
        while(t--){
            int n,x,y,maxh; cin>>n>>x>>y>>maxh;
    
            for(int i=1;i<=n+2;i++) dp[i][0]=dp[i][1]=inf;
            dp[1][1] = dp[1][0] = 0;
    
            for(int i=1;i<=n;i++){
                int l,r,h; cin>>l>>r>>h; plats[i] = node(l,r,h);
            }
            plats[n+1]=node(-inf,inf,0);
            plats[n+2]=node(x,x,y);   //for(int i=1;i<=n+2;i++) cout<<plats[i].l<<" "<<plats[i].r<<" "<<plats[i].h<<endl; cout<<endl;
            sort(plats+1,plats+1+n+2,cmp);
    
        
           //for(int i=1;i<=n+2;i++) cout<<plats[i].l<<" "<<plats[i].r<<" "<<plats[i].h<<endl;
    
            for(int i=2;i<=n+2;i++){
                for(int j=i-1;j>=1;j--){//往左
                    if( plats[i].l>=plats[j].l && plats[i].l<=plats[j].r && plats[i].h-plats[j].h<=maxh ){
                        if( j==1 ) dp[i][0]=plats[i].h-plats[j].h;
                        else{
                            int x1=dp[j][0]+ plats[i].l-plats[j].l,x2=dp[j][1]+ plats[j].r-plats[i].l;
                            int plus = min(x1,x2);
                            dp[i][0] = plats[i].h-plats[j].h + plus;
                            //cout<<plats[i].h<<" "<<plats[j].h<<" "<<x1<<" "<<x2<<endl;
                            break;
                        }
                    }
                }
    
                for(int j=i-1;j>=1;j--){
                    if( plats[i].r>=plats[j].l && plats[i].r<=plats[j].r && plats[i].h-plats[j].h<=maxh ){
                        if(j==1) dp[i][1] = plats[i].h-plats[j].h;
                        else{
                            int x1=dp[j][0]+ plats[i].r-plats[j].l,x2=dp[j][1]+ plats[j].r-plats[i].r;
                            int plus = min(x1,x2);
                            dp[i][1] = plats[i].h-plats[j].h + plus;
                            break;
                        }
                    }
                }
               // cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<endl;
    
            }
            cout<<dp[n+2][0]<<endl;
    
        }
    
        return 0;
    }
    View Code

    POJ 3186 Treats for the Cows

    状态定义比较难想,想到就好做了

    dp[i][j]为从i卖到j的最大价值(此最大价值指的是以1-n为两端的最大值,而不是以i-j为两端)

    当卖到还剩i-j的时候,这时候再卖i或j是在第n-j+i天卖,所以

    dp[i][j] = max( dp[i+1][j] + (n-j+i)*value[i] , dp[i][j-1]+(n-j+i)*value[j] )

    base case是i==j的时候,拿递归会比较好写

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 2e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    
    int memo[maxn][maxn],value[maxn],n;
    int dp(int i,int j){
        if( memo[i][j]!=-1 ) return memo[i][j];
        if( i==j ) return n*value[i];
        return memo[i][j] = max( dp(i+1,j)+value[i]*(n-j+i), dp(i,j-1)+value[j]*(n-j+i) );
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        while( scanf("%d",&n)!=EOF ){
            memset(memo,-1,sizeof(memo));
            for(int i=1;i<=n;i++) cin>>value[i];
            printf("%d
    ",dp(1,n));
        }
        
        
    
        return 0;
    }
    View Code

    HDU 2859 Phalanx

    dp[i][j]为以(i,j)为矩阵最左下点的最大对称矩阵

    那么按照对称的要求,(i,j)右侧点应与其对应的上侧点相同,我们设有len个相同

    那么dp[i][j] = dp[i-1][j+1] + 1 如果 len>=dp[i-1][j+1]

    else dp[i][j] = len+1,想一下就好了

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    char maze[maxn][maxn];
    int dp[maxn][maxn];
    
    int main(){
        int n;
        while( scanf("%d",&n)!=EOF ){
            if(n==0) break;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++) cin>>maze[i][j];
    
            for(int i=1;i<=n;i++) dp[1][i] = dp[i][n] = 1;
    
            for(int i=2;i<=n;i++){
                for(int j=1;j<n;j++){
                    int len=0;
                    //上侧与右侧比
                    for(int k=1;k<=n;k++){
                        if( i-k<1 || j+k>n ) break;
                        if( maze[i-k][j]!=maze[i][j+k] ) break;
                        len++;
                    }
                    if( len>=dp[i-1][j+1] ) dp[i][j]=dp[i-1][j+1]+1;
                    else dp[i][j] = len+1;
                }
            }
    
            int ans=1;
            for(int i=2;i<=n;i++)
                for(int j=1;j<n;j++) ans=max(ans,dp[i][j]);
            
            cout<<ans<<endl;
        }
    
        return 0;
    }
    View Code

    POJ 3616 Milking Time

    先排序,然后dp[i]代表在i时间段挤奶的最大挤奶量,那么

    dp[i] = max( dp[k] + i时间段的efficiency ) k为所有在i之前且不与i冲突的时间段

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 1e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int rest,dp[maxn];//dp[i]为在第i个时间段的最大挤奶量
    struct node{
        int l,r,value;
        node(int l1=0,int r1=0,int v1=0): l(l1),r(r1),value(v1) {}
    }milking[maxn];
    
    bool cmp(node n1,node n2){
        return n1.r<n2.r;
    }
    
    int main(){
    
        int n,m;
        while( scanf("%d%d%d",&n,&m,&rest)!=EOF ){
            for(int i=1;i<=m;i++){
                int l,r,value; scanf("%d%d%d",&l,&r,&value);
                milking[i] = node(l,r,value);
            }
            sort(milking+1,milking+1+m,cmp);
            for(int i=1;i<=m;i++) dp[i]=milking[i].value;
    
            //for(int i=1;i<=m;i++) cout<<milking[i].l<<" "<<milking[i].r<<" "<<milking[i].value<<endl;
           int ans=0;
           
            for(int i=1;i<=m;i++){
                for(int j=1;j<i;j++){
                    if( milking[j].r+rest<=milking[i].l ) dp[i] = max( dp[i],dp[j]+milking[i].value );
                    ans=max(ans,dp[i]);
                }
            }
            cout<<ans<<endl;
        }
    
        return 0;
    }
    View Code

    POJ 3666 Making the Grade

    dp[i][j]代表前i个坡,且第i个坡的高度为j的情况下,的最小代价

    dp[i][j] = min( dp[i-1][k]  ) + abs(j-a[i]) k为1-j(所有小于等于j的高度)

    所以可以利用前缀优化掉j复杂度的时间,但是j的高度可能很高,所以大胆贪心猜个结论最终的土坡高度一定源于a数组里的某个数,所以离散化做一下就ok了

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 998244353;
    const int maxn = 2e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    ll dp[maxn][maxn];
    int a[maxn],b[maxn];
    
    int main(){
        int n; cin>>n;
        for(int i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; }
        sort(b+1,b+1+n);
    
        for(int i=1;i<=n;i++){//从第1个土堆到第i个土堆
            ll m=dp[i-1][1];
            for(int j=1;j<=n;j++){//第i个土堆的高度为j时的最小cost
                m=min(m,dp[i-1][j]);
                dp[i][j] = abs( a[i]-b[j] ) + m;
            }
        }
    
        ll ans=inf;
        for(int i=1;i<=n;i++) ans=min(ans,dp[n][i]);
        cout<<ans<<endl;
    
        return 0;
    }
    View Code
  • 相关阅读:
    PHP之html~01
    常用的html标签大全
    PHP从PHP5.0到PHP7.1的性能全评测
    PHP的性能演进(从PHP5.0到PHP7.1的性能全评测)
    Unix及类Unix系统文本编辑器的介绍
    keychain 的学习
    开发中遇到的一些问题
    IOS 照片浏览器总结(思想步骤)
    IOS 应用管理(九宫格) 总结笔记
    IOS 集成友盟分享
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/10222676.html
Copyright © 2020-2023  润新知