zoukankan      html  css  js  c++  java
  • 简单区间dp

    题目链接

    对于基本区间dp,设dp[l][r]是区间l到r的最大价值。

    我们可以枚举区间的长度,在枚举左端点,判断即可。

    当右端点大于n,就break。

    dp[l][r]=max(dp[l+1][r]+v[l]*(n-i+1),dp[l][r-1]+v[r]*(n-i+1))

    别忘了初始化,dp[i][i]=n*v[i].

    代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 int dp[3000][3000];
     5 int n,v[3000];
     6 int main(){
     7     scanf("%d",&n);
     8     for(int i=1;i<=n;++i)
     9         scanf("%d",&v[i]);
    10     for(int i=1;i<=n;++i)dp[i][i]=n*v[i];
    11     for(int i=2;i<=n;++i){
    12         for(int l=1;l<=n;l++){
    13             int r=l+i-1;
    14             if(r>n)break;
    15             dp[l][r]=max(dp[l+1][r]+v[l]*(n-i+1),dp[l][r-1]+v[r]*(n-i+1));
    16         }
    17     }printf("%d
    ",dp[1][n]);
    18     return 0;
    19 }
    View Code

     题目链接

    也可以用区间dp做。

    设dp[i][j]是区间[i,j]的最佳答案。

    我们枚举区间。

    对于每次区间,枚举合并的交点。

    当且仅当合并两方值相等时才能合并。

    if(dp[i][k]==dp[k+1][j])dp[i][j]=max(dp[i][j],dp[i][k]+1)

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int n,dp[500][500],ans=-(1<<30);
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",&dp[i][i]);
        for(int i=n-1;i>=1;--i){
            for(int j=i+1;j<=n;++j){
                for(int k=i;k<j;++k)
                    if(dp[i][k]==dp[k+1][j])//可以合并 
                        dp[i][j]=max(dp[i][j],dp[i][k]+1);//dp[i][k]&dp[k+1][j]'s merge
                ans=max(ans,dp[i][j]);//max
            }
        }printf("%d
    ",ans);
        return 0;
    } 
    View Code

     题目链接

    本题与上一题类似,发现样例都一样。

    但是神奇的数据范围不得不让我们重新考虑转移方程。

    一看每个数都小于等于40,那么很容易想到dp中的一维表示合并的数。

    看题解后恍然大悟,设dp[i][j]是能够合并成i且左端点为j的右端点的位置。

    那么,dp[i][j]=dp[i-1][dp[i-1][j]]

    即,当一个数可以合并成i时,一定是由i-1合并出来的。

    我们画一个数轴,其中j在左边。

    那么,能合并出i-1的位置就在dp[i-1][j]

    那么继续往后,在合并出一个i-1,那么这个位置显然是:

    dp[i-1][dp[i-1][j]]

    至此,得到转移方程。

    那么,表示数字时,我们只需要开到58.

    首先,因为数字小于等于40.其次,

    262144=218.

    用类似倍增的思想,来倍增2.

    于是我们要开到58.(也可以用运气法开个大的)

    代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #define MAXN 262144+100
     4 using namespace std;
     5 int n,a[MAXN],ans;
     6 int dp[59][MAXN];
     7 int main(){
     8     scanf("%d",&n);
     9     for(int i=1;i<=n;++i)scanf("%d",&a[i]),dp[a[i]][i]=i+1;
    10     for(int i=2;i<=58;i++){
    11         for(int j=1;j<=n;j++){
    12             if(!dp[i][j])dp[i][j]=dp[i-1][dp[i-1][j]];
    13             if(dp[i][j])ans=max(ans,i);
    14         }
    15     }printf("%d
    ",ans);
    16     return 0;
    17 }
    View Code

     题目链接

    我们可以先断环为链,然后枚举右端点。相应的,左端点从右端点依次枚举到1.

    那么,设dp[i][j]是区间【i,j】合并的最优策略,则有:

    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1])

    我们在i与j之间枚举k做断点,后面部分就是新合并后的价值。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    struct node{
        int l,r;//l from r to
    }q[300];
    int n,a[300],maxn;
    int dp[300][300];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]),a[i+n]=a[i];
        for(int j=2;j<=(n<<1);++j){//枚举右端点 
            for(int i=j-1;j-i<n&&i>=1;i--){//向左依次枚举区间 
                for(int k=i;k<j;++k){//枚举断点 
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]);
                    maxn=max(dp[i][j],maxn);//每次合并=max(左区间值+右区间值+新合并的值) 
                }//取max 
            }
        }
        printf("%d
    ",maxn);
        return 0;
    }
    View Code
  • 相关阅读:
    AIMS 2013中的性能报告工具不能运行的解决办法
    读懂AIMS 2013中的性能分析报告
    在线研讨会网络视频讲座 方案设计利器Autodesk Infrastructure Modeler 2013
    Using New Profiling API to Analyze Performance of AIMS 2013
    Map 3D 2013 新功能和新API WebCast视频下载
    为Autodesk Infrastructure Map Server(AIMS) Mobile Viewer创建自定义控件
    ADN新开了云计算Cloud和移动计算Mobile相关技术的博客
    JavaScript修改css样式style
    文本编辑神器awk
    jquery 开发总结1
  • 原文地址:https://www.cnblogs.com/h-lka/p/11146515.html
Copyright © 2011-2022 走看看