zoukankan      html  css  js  c++  java
  • 动态规划杂题

    概率期望dp转这里 http://www.cnblogs.com/L-Memory/p/7352637.html

    树形dp转这里 http://www.cnblogs.com/L-Memory/p/7470228.html

     

    bzoj1705 (poj3602)

    题意:有一列树,总共有n(n<=100000)棵,给定树的高度,要在相邻树和树之间架设电线,代价为c*(两树的高度差的绝对值),

    但可以增高树的高度,需要代价为<增高高度的平方>

    求最小代价

    思路:f[i][j] = min(f[i-1][k]+|j-k|*c+(a[i]-j)*(a[i]-j))

    摘出与k无关项得
    f[i][j] = min(f[i-1][k]+|j-k|*c) + (a[i]-j)*(a[i]-j)
    记P = min(f[i-1][k]+|j-k|*c) , Q = (a[i]-j)*(a[i]-j)
    则f[i][j] = P + Q
    P = min(A,B),其中
    A = min(f[i-1][k]+(j-k)*c) (k<=j)
    B = min(f[i-1][k]+(k-j)*c) (k>j)
    A = min(f[i-1][k]-k*c) + j*c
    B = min(f[i-1][k]+k*c) - j*c
    记C[X][i] = min(f[X][k] - k*c) k∈[1,i]
    记D[X][i] = min(f[X][k] + k*c) k∈[i,n]
    则A = C[i-1][j] + j*c
    则B = D[i-1][j+1] - j*c
    显然C、D在任何时刻只需保存X=i-1一行的值
    注意高度只能增高,所以h[i]∈[a[i],100]
    利用辅助数组优化后,时间复杂度降为O(N*100)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 100010
    #define H 110
    #define inf 0x3f3f3f3f
    
    using namespace std;
    int n,c,h,P,Q,A,B;
    int a[N],C[H],D[H],f[N][H];
    
    int main()
    {
        scanf("%d%d",&n,&c);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        h=a[1];
        for(int i=1;i<=n;i++) h=max(h,a[i]);
        h=min(h,100);
        memset(f,0x3f,sizeof f);
        for(int i=1;i<=n;i++)
        {
            if(i==1) 
              for(int j=a[i];j<=h;j++)
                f[1][j]=(j-a[1])*(j-a[1]);
            else
            {
                for(int j=a[i];j<=h;j++)
                {
                    Q=(j-a[i])*(j-a[i]);
                    A=C[j]+j*c;
                    B=D[j+1]-j*c;
                    P=min(A,B);
                    f[i][j]=P+Q;
                }
            }
            C[0]=D[h+1]=inf;
            for(int j=1;j<=h;j++) C[j]=min(C[j-1],f[i][j]-j*c);
            for(int j=h;j>=1;j--) D[j]=min(D[j+1],f[i][j]+j*c);
        }
        int ans=inf;
        for(int i=1;i<=h;i++) ans=min(ans,f[n][i]);
        printf("%d
    ",ans);
        return 0;
        return 0;
        return 0;
    }
    Code

     

    poj3666

    题意:

    给定一个序列,以最小代价将其变成单调增或单调减序列,代价看题目公式。

    思路:dp[i][j]表示第i个数高度为第j个时的代价  

    假设前几个数组成的最大值为β,我们要考虑从0-β的改变值,然后不断推到第n个序列。

    显然,这样的复杂度为0(Nβ^2),当然这样的复杂度显然是出事的。  

    像之前那样扫描的话,那么其实忽略了一个很重要的事实,就是在变到α(α<β),其实对于α+1~β之内不会对α造成影响,于是可以用一个最小值的临时变量储存在α之前的最小值,用这个更新dp即可,那样就少了一次扫β的复杂度复杂度变为O(Nβ);但β实在是太大了。

    所以要离散化

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    
    #define Abs(a) ((a)>0?(a):-(a))
    #define Mod(a,b) (((a)-1+(b))%(b)+1)
    
    using namespace std;
    const int N=2005;
    const long long inf=0x7f7f7f7f;
    int n;
    int a[N],b[N];
    long long int dp[N][N];
    
    void solve()
    {
        for(int i=1;i<=n;i++)
        {
            long long mn=dp[i-1][1];
            for(int j=1;j<=n;j++)
            {
                mn=min(mn,dp[i-1][j]);
                dp[i][j]=Abs(a[i]-b[j])+mn;
            }
        }
        long long ans=dp[n][1];
        for(int i=1;i<=n;i++)
            ans=min(ans,dp[n][i]);
        printf("%lld
    ",ans);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        solve();
        return 0;
    }
    Code

     

    codevs2205

    题意:小C的老师给了他一个长度为N的数字序列,每个位置有一个整数,他需要小C帮他找到这个数字序列里面有多少个等差数列。

    思路:f[i][j]表示以i为终点(且i不为起点),差为j的个数

    穷举起点和第二项,然后更新第二项的值。
    最后就需要加上n,以为一个元素也是等差数列。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 1007
    #define mod 9901
    
    using namespace std;
    int n,m,d,a[N];
    long long ans,f[N][N<<2];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
          for(int j=i+1;j<=n;j++)
            {
                d=a[j]-a[i]+1000;
                f[j][d]=(f[i][d]+f[j][d]+1)%mod;
            }
        ans=n;
        for(int i=1;i<=n;i++)
          for(int j=0;j<=2000;j++)
            ans=(ans+f[i][j])%mod;
        printf("%lld
    ",ans);
        return 0;
        return 0;
        return 0;
    }
    Code

     

    codevs1253

    题目描述 Description

    某人喜欢按照自己的规则去市场买菜,他每天都列一个买菜的清单,自由市场的菜码放也有一个顺序,该人有一个特点,就是按顺序买菜,从不走回头路,当然,她希望能花最好的钱买到所有的菜,你能帮帮他吗?

    思路:比较简单的dp,f[i][j]表示市场上的菜单到i需购买的菜单到j所需的最少花费,若i=j,则更新这个点,否则f[i][j]=f[i-1][j]。初始化将f[i][0]=0;其他都付最大值,最后比较f[n][m]和0x7fffffff,若大于则是impossible,否则输出。
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    int pos[100001],pos1[101];
    double f[100001][101],val[1000001];
    int n,m,ans;
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) 
          scanf("%d",&pos1[i]);
        for(int i=1;i<=m;i++) 
          scanf("%d%lf",&pos[i],&val[i]);
        memset(f,127,sizeof f);
        for(int i=1;i<=m;i++) f[i][0]=0;
        for(int i=1;i<=m;i++)
          for(int j=1;j<=n;j++)
            {
                f[i][j]=f[i-1][j];
                if(pos[i]==pos1[j]) 
                  f[i][j]=min(f[i][j],f[i-1][j-1]+val[i]);
            }
        if(f[m][n]>=0x7fffffff) printf("Impossible
    ");
        else
          printf("%.2f
    ",f[m][n]);
        return 0;
        return 0;
        return 0;
    }
    Code

     codevs1959

    题目描述 Description

    一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,要求两个组的人数相差不能超过1,且两个组内的所有人体重加起来尽可能地接近。

    思路:二维费用背包模型

    设dp[i][j][k]表示前i个人选j个能否得到体重为k。
    这样dp的转移有2种,不选(dp[i-1][j][k]),选(dp[i-1][j-1][k-w[i]])(w表示体重),
    得到dp数组以后,再枚举可能的答案,然后求的差值最小的,得到答案

    /*
    二维费用背包模型
    设dp[i][j][k]表示前i个人选j个能否得到体重为k。
    这样dp的转移有2种,不选(dp[i-1][j][k]),选(dp[i-1][j-1][k-w[i]])(w表示体重),
    得到dp数组以后,再枚举可能的答案,然后求的差值最小的,得到答案 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    bool dp[51][45000];
    int n,w[111],s;
    int main()
    {
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++)  scanf("%d",&w[i]),s += w[i];
        dp[0][0] = 1;
        int k = (n+1)/2;
        for(int i = 1;i <= n;i ++)
          for(int j = k-1;j >= 0;j --)
            for(int v = 450*i;v >= 0;v --)
                if(dp[j][v])
                    dp[j+1][v+w[i]] = 1;
        int minn=1<<30;
        n = 0;
        for(int i = 0;i <= 450*k;i ++)
        {
            if(dp[k][i] && abs(s - i*2) < minn) //看成s-i和i的差值 
            {
                minn = abs(s-i*2);
                if(i <= s/2)    n = i;
                else    n = s-i;
            }
        }
        printf("%d %d",n,s-n);
        return 0;
    }
    Code

     

     codevs1043

    题目描述 Description

    设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):

     

    某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。

    此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

    思路:

    设f[x1][y1][x2][y2]表示第一个人走到x1,y1点,第二个人走到x2,y2点的最优解。

    方程为 f[x1][y1][x2][y2] =max(f[x1][y1][x2][y2],f[x1-1][y1][x2-1][y2],f[x1-1][y1][x2][y2-1],f[x1][y1-1][x2-1][y2],f[x1][y1-1][x2]

    [y2-1]) + G[x1][y1] 若 x1 != x2 || y1 != y2,还应加上G[x2][y2]。即必须累加当前走过的路径上的数字,为防止两个人同时走到一个点而累加两次产生错误答案,判定一下。

    #include <cstdio>
    #include <iostream>
    
    using namespace std;
    int map[99][99];
    int dp[55][55][55][55];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        while(1)
        {
            int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
            if(!x&&!y&&!c) break;
            map[x][y]=c;
        }
        for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++)
          for(int k=1;k<=n;k++)
           for(int l=1;l<=n;l++)
            {
                int s1=max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]);
                int s2=max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]);
                int pls=map[i][j]+map[k][l];
                if(i==k&&j==l)
                 pls-=map[i][j];
                dp[i][j][k][l]=max(s1,s2)+pls;
            }
        printf("%d",dp[n][n][n][n]);
        return 0;
    }
    Code

     

    codevs1196   几乎同上

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 51
    
    using namespace std;
    int dp[N][N][N][N],a[N][N];
    int n,m,ans;
    
    void DP ()
    {
        int ans1,ans2;
        for(int i=1;i<=m;i++)
          for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++)
              for(int l=1;l<=n;l++)
                {
                    ans1=max(dp[i-1][j][k-1][l],dp[i][j-1][k][l-1]);
                    ans2=max(dp[i-1][j][k][l-1],dp[i][j-1][k-1][l]);
                    dp[i][j][k][l]=max(ans1,ans2)+a[i][j]+a[k][l];
                    if(i==k && j==l) dp[i][j][k][l]-=a[i][j];
                }
        ans=dp[m][n][m][n];
    }
    
    int main()
    {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++)
          for(int j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
        DP();
        printf("%d
    ",ans);
        return 0;
    }
    Code

     

     codevs3369

    题目描述 Description

    神牛有很多…当然…每个同学都有自己衷心膜拜的神牛.
    某学校有两位神牛,神牛甲和神牛乙。新入学的N位同学们早已耳闻他们的神话。所以,已经衷心地膜拜其中一位了。
    现在,老师要给他们分机房。
    但是,要么保证整个机房都是同一位神牛的膜拜者,或者两个神牛的膜拜者人数差不超过M。
    另外,现在N位同学排成一排,老师只会把连续一段的同学分进一个机房。老师想知道,至少需要多少个机房。

    思路:预处理一下前i位置有多少个两个神牛的崇拜者(前缀和数组),然后就是dp部分了,f[i]表示分到i个人最少多少个教室,循环i和k(i-k表示最后一个教室的人数),更新f[i]的值。预处理:f[0]=0;
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    
    #define N 2501
    
    using namespace std;
    int dp[N],tot1[N],tot2[N],a[N];
    int n,m,cnt1,cnt2,ans;
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==1) tot1[i]=1;
            else tot2[i]=1;
        }
        for(int i=1;i<=n;i++)tot1[i]+=tot1[i-1];        
        for(int i=1;i<=n;i++)tot2[i]+=tot2[i-1];
        memset(dp,0x3f,sizeof dp);dp[0]=0;
        for(int i=1;i<=n;i++)
          for(int j=0;j<i;j++)  
            {
                if(abs((tot1[i]-tot1[j])-(tot2[i]-tot2[j]))<=m || 
                tot1[i]-tot1[j]==0 ||tot2[i]-tot2[j]==0)
                  dp[i]=min(dp[i],dp[j]+1);
            }
        printf("%d
    ",dp[n]);
        return 0;
    }
    Code

     

    codevs1090

    题目描述 Description

    设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:

    subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数

    若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空

    子树。试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;

    (1)tree的最高加分

    (2)tree的前序遍历

    姿势比较奇特
    二叉树的中序遍历是把根节点放在中间
    换而言之就是把根节点左右两边的树形序列(子树)合并起来
    那么很明显这道题就是一个合并类的区间DP了
    和石子合并思路相同,需要注意的是初始状态必须为1(因为是相乘),不然结果会出错
    dp[i][j]表示中序遍历i到j最大值
    方程:dp[i,j]:=max(dp[i][k-1]*dp[k+1][j]+dp[k][k]

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 101
    
    using namespace std;
    int n,num[N][N];
    long long f[N][N];
    
    void find(int x,int y)
    {
        if(x<=y)
        {
            printf("%d ",num[x][y]);
            find(x,num[x][y]-1);
            find(num[x][y]+1,y);
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=0;i<=n;i++) for(int j=0;j<=n;j++)
        {
            f[i][j]=1;num[i][i]=i;
        }
        for(int i=1;i<=n;i++) scanf("%d",&f[i][i]);
        for(int i=n;i>=1;i--)
          for(int j=i+1;j<=n;j++)
            for(int k=i;k<=j;k++)
              {
                  if(f[i][j]<(f[i][k-1]*f[k+1][j]+f[k][k]))
                    f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k],
                    num[i][j]=k;
    
              }
        printf("%lld
    ",f[1][n]);find(1,n);
        return 0;
    }
    Code

     

    bzoj 1084 最大子矩阵

    题意:这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵
    不能相互重叠。

    思路:

    这题因为m只有1、2两种情况,所以分开讨论。
    m=1 这部分还是比较水的,f[i][j]表示前i行选j个矩形的最大分值。
    方程f[i][j]=max(f[i-1][j],max{f[k][j-1]+sum[i]-sum[k]|0<=k<=i})(sum是前缀和)
    * m=2
    f[i][j][k]表示第一列前i行,第二列前j行选k个矩形的最大分值,方程和m=1是较相似,但多了i=j时的情况,
    叙述比较麻烦,具体看代码。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 105
    
    using namespace std;
    int dp[N][N][N],f[N][N],s[N][2],sum[N],a[N],b[N];
    int n,m,k,ans;
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        if(m==1)
        {
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                sum[i]=sum[i-1]+a[i];
            }
            for(int i=1;i<=n;i++)
            {
                f[i][0]=0;
                for(int j=1;j<=k;j++)
                {
                    f[i][j]=f[i-1][j];
                    for(int l=1;l<=i;l++)
                      f[i][j]=max(f[i][j],f[l][j-1]+sum[i]-sum[l]);
                }
            }
            printf("%d
    ",f[n][k]);
        }    
        
        else
        {
            for(int i=1;i<=n;i++)
            {
                scanf("%d%d",&a[i],&b[i]);
                s[i][0]=s[i-1][0]+a[i];
                s[i][1]=s[i-1][1]+b[i];
            }
            for(int i1=1;i1<=n;i1++) for(int i2=1;i2<=n;i2++)
            {
                dp[i1][i2][0]=0;
                for(int j=1;j<=k;j++)
                {
                    dp[i1][i2][j]=max(dp[i1-1][i2][j],dp[i1][i2-1][j]);
                    for(int l=0;l<i1;l++)
                      dp[i1][i2][j]=max(dp[i1][i2][j],dp[l][i2][j-1]+s[i1][0]-s[l][0]);
                    for(int l=0;l<i2;l++)
                      dp[i1][i2][j]=max(dp[i1][i2][j],dp[i1][l][j-1]+s[i2][1]-s[l][1]);
                    if(i1==i2)
                    {
                        for(int l=0;l<i1;l++)
                          dp[i1][i2][j]=max(dp[i1][i2][j],dp[l][l][j-1]+s[i1][0]-s[l][0]+s[i2][1]-s[l][1]);
                    }
                }
            }
            printf("%d
    ",dp[n][n][k]);
        }
        return 0;
    }
    Code

     

    bzoj1079 着色方案

    题意:有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。
    所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两
    个相邻木块颜色不同的着色方案。

    思路:

    先确定这是一道dp
    再看范围得结论这是一道多维dp,想起了王八棋。
    然后定状态,很多维的话只能一种一维咯,因为要转移所以还要加一维
    f[a][b][c][d][e][k] 铅丝维是种类最后是上一块放的是第几种
    转移略麻烦,比如这次三块的减了一,两块的就加了一,乘的时候就要减回来。
    乘法原理,自行体会,,,,,,

    #include<iostream>
    #include<cstdio>
    
    #define ll long long
    #define mod 1000000007
    
    using namespace std;
    ll f[16][16][16][16][16][6];
    int x[6],n;
    bool L[16][16][16][16][16][6];
    
    ll dp(int a,int b,int c,int d,int e,int k)
    {
        ll t=0;
        if(L[a][b][c][d][e][k])return f[a][b][c][d][e][k];
        if(a+b+c+d+e==0)return 1;
        if(a)  
            t+=(a-(k==2))*dp(a-1,b,c,d,e,1  );
        if(b)  
            t+=(b-(k==3))*dp(a+1,b-1,c,d,e,2);  
        if(c)  
            t+=(c-(k==4))*dp(a,b+1,c-1,d,e,3);  
        if(d)  
            t+=(d-(k==5))*dp(a,b,c+1,d-1,e,4);  
        if(e)  
            t+=e*dp(a,b,c,d+1,e-1,5);  
        L[a][b][c][d][e][k]=1;
        return f[a][b][c][d][e][k]=(t%mod);
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int t;
            scanf("%d",&t);
            x[t]++;
        }
        printf("%lld",dp(x[1],x[2],x[3],x[4],x[5],0));
        return 0;
    } 
    Code

     

    bzoj1046 上升子序列

    题目:对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax
    2 < … < axm)。那么就称P为S的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。任务给
    出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先
    x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.

    思路:

    我们知道nlogn的最长上升子序列中定义状态f[i]表示以i结尾的...
    这个题要求以i开头的 所以倒着做最长下降子序列就好了,f只记录长度
    所以需要有个best数组存序列
    最后输出答案,若要求的序列长度为x,如果以第一个数(字典序最小的数)
    开头的最长上升子序列大等于x,则将它放在答案第一个,
    第二个数开头小于x,则舍弃,第三个大于x-1,放答案第二个,以此类推

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    #define inf 1000000000
    #define N 100007
    
    using namespace std;
    int n,m,cnt;
    int a[N],f[N],best[N];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    void solve(int x)
    {
        int last=0;
        for(int i=1;i<=n;i++)
            if(f[i]>=x&&a[i]>last)
            {
                printf("%d",a[i]);
                if(x!=1)printf(" ");
                last=a[i];x--;
                if(!x)break;
            }printf("
    ");
    }
    
    int find(int x)
    {
        int l=1,r=cnt,ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(best[mid]>x)ans=mid,l=mid+1;
            else r=mid-1;
        }return ans;
    }
    
    void dp()
    {
        for(int i=n;i;i--)
        {
            int t=find(a[i]);
            f[i]=t+1;cnt=max(cnt,t+1);
            if(best[t+1]<a[i]) best[t+1]=a[i];
        }
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        dp(); m=read();
        for(int i=1;i<=m;i++)
        {
            int x=read();
            if(x<=cnt)solve(x);
            else puts("Impossible");
        }
        return 0;
    }
    Code

     

    bzoj1019 汉诺塔

    题意:普通汉诺塔加了优先级

    思路:优先级可以预处理

    如果是普通汉诺塔
    操作就是将第一个柱子除底盘外的移到第二个柱子,然后把底盘移到第三个柱子,然后把第二个柱子的盘子移动到第三个
    但基本的汉诺塔问题的操作是没有限制的,就是你想移哪儿移哪儿,但是这题不一样,这题强制了一个操作优先级,所以要用不同的方法去做。
    f[x][i]: 第x号柱子移i个盘子到最优柱子的最优解
    p[x][i]:第x号柱子移i个盘子到p[x][i]号柱子是最优解
    那么就有两种情况,第一种就是普通的汉诺塔移动
    f[a][i]=f[a][i-1]+1+f[b][i-1];
    另外一种就是特殊的
    a上i-1个盘子移至b上,将a上的第i个盘子,移至c。由于i个盘子还没叠到一起,所以接下来还要再次移动b上的i-1个
    如果移到c的话就是经典算法
    如果i-1个盘子移到a的话,那么最终就是移到b柱子上
    f[a][i]=f[a][i-1]+1+f[b][i-1]+1+f[a][i-1];

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    
    #define ll long long 
    
    using namespace std;
    int p[4][31],x[10],y[10];
    int n; char s[3];
    ll f[4][31];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=6;i++)
        {
            scanf("%s",s);
            x[i]=s[0]-'A'+1,y[i]=s[1]-'A'+1;
        }
        for(int i=6;i>=1;i--) p[x[i]][1]=y[i];//倒着来的原因是优先级高的会覆盖优先级低的,被覆盖的我们就不要了
        for(int i=1;i<=3;i++) f[i][1]=1LL;
        for(int i=2;i<=n;i++)
        {
            for(int a=1;a<=3;a++)
            {
                int b=p[a][i-1],c=6-a-b;//c是什么?一号二号三号加起来是6嘛,减去其他两个不就是剩下那个了?
                if(p[b][i-1]==c)
                {
                    f[a][i]=f[a][i-1]+1+f[b][i-1];//1是底盘移动的步数
                    p[a][i]=c;
                }
                else if(p[b][i-1]==a)
                {
                    f[a][i]=f[a][i-1]+1+f[b][i-1]+1+f[a][i-1];
                    p[a][i]=b;
                }
            }
        }
        printf("%lld
    ",f[1][n]);
        return 0;
    }
    Code

     

    bzoj 1260 [CQOI2007]涂色paint

    题意:求将一段序列染成指定颜色的最小步数

    思路:

    区间dp 类似石子归并有木有
    f[i][j] 将1~n染色的最小步数
    从小区间穷举区间后,分情况讨论
    如果区间两端相等,就取[i+1,j],[i,j-1],[i+1,j-1]+1中的最小值;
    如果不一样,就穷举中间断点k,取min([i,k][k+1,j])。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 51
    
    using namespace std;
    int f[N][N],n,m,cnt;
    char s[N];
    
    int main()
    {
        memset(f,127/3,sizeof f);
        scanf("%s",s);n=strlen(s);
        for(int i=n;i>=1;i--) s[i]=s[i-1];
        for(int i=1;i<=n;i++) f[i][i]=1;
        for(int i=n-1;i>=1;i--)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(s[i]==s[j]) 
                {
                    f[i][j]=min(f[i+1][j],f[i][j-1]);
                    if(i<j-1)f[i][j]=min(f[i][j],f[i+1][j-1]+1);
                }
                else 
                for(int k=i;k<=j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
            }
        }
        printf("%d
    ",f[1][n]);
        return 0;
    }
    Code

     

     bzoj1089 [SCOI2003]严格n元树

    题意:求深度为d的严格n元树

    思路:

    定义S[i]代表深度<=i的严格n元树的个数
    那么最后S[d]-S[d-1]就是答案
    那么对于S[i],我们由S[i-1]递推来,
    我们考虑新加一个根节点,然后根节点有n个子节点,每个子节点都可以建一颗深度<=i-1的树,那么每个
    子节点都有S[i-1]种选法,那么n个子节点就有S[i-1]^n选法,再加上都不选,就是深度为0的情况
    那么S[i]:=(S[i-1]^n)+1;

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<iomanip>
    using namespace std;
    struct long_int{
        int num[3000],cnt;
        void operator = (int y)
        {
            num[1]=y;cnt=1;
        }
        int& operator [] (int x)
        {
            return num[x];
        }
    }S[200];
    void operator *= (long_int &x,long_int &y)
    {
        long_int z=S[199];
        int i,j;
        for(i=1;i<=x.cnt;i++)
            for(j=1;j<=y.cnt;j++)
            {
                z[i+j-1]+=x[i]*y[j];
                z[i+j]+=z[i+j-1]/10000;
                z[i+j-1]%=10000;
            }
        z.cnt=x.cnt+y.cnt;
        if(!z[z.cnt])--z.cnt;
        x=z;
    }
    void operator ++ (long_int &x)
    {
        int i=1;x[1]++;
        while(x[i]==10000)x[i]=0,x[++i]++;
    }
    long_int operator - (long_int &x,long_int &y)
    {
        long_int z=S[199];
        int i;
        for(i=1;i<=x.cnt;i++)
        {
            z[i]+=x[i]-y[i];
            if(z[i]<0) z[i]+=10000,z[i+1]--;
            if(z[i]) z.cnt=i;
        }
        return z;
    }
    long_int operator ^ (long_int x,int y)
    {
        long_int z=S[199];z=1;
        while(y)
        {
            if(y&1) z*=x;
            x*=x;y>>=1;
        }
        return z;
    }
    ostream& operator << (ostream &os,long_int x)
    {
        int i;
        os<<x[x.cnt];
        for(i=x.cnt-1;i;i--)
            os<<setfill('0')<<setw(4)<<x[i];
            //os<<x[i];
        return os;
    }
    int n,d;
    int main()
    {
        int i;
        cin>>n>>d;
        if(!d)
        {
            puts("1");return 0;
        }
        S[0]=1;
        for(i=1;i<=d;i++)
            S[i]=S[i-1]^n,++S[i];
        cout<<S[d]-S[d-1]<<endl;
    }
    Code

     

    bzoj1037[ZJOI2008]生日聚会Party

    题意:求n个男生m个女生放一排任意一段差值不超过k个的方案数

    思路:

    开始想状态dp[i][j][k]:前i个人中j男k女方案数
    但貌似不会转移 因为有任意一段差值不大于k的限制 考虑如何在状态中把限制表现出来
    又前i个人不是男就是女(废话) 知道男就能推出女
    所以令dp[i][j][x][y]:前i个人中有j个是男生,任意一段男生比女生最多多x人,女生比男生最多多y人的方案数
    转移时显然四重循环 枚举多的人数

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    
    #define mod 12345678
    #define N 157
    
    using namespace std;
    int f[N<<1][N][21][21],n,m,ans,cnt,k;
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        f[0][0][0][0]=1;
        for(int i=0;i<=n+m-1;i++)
          for(int j=0;j<=n;j++)
            for(int x=0;x<=k;x++)
              for(int y=0;y<=k;y++)
               if(f[i][j][x][y])
               {
                         if(j+1<=n&&x+1<=k) (f[i+1][j+1][x+1][max(y-1,0)]+=f[i][j][x][y])%=mod;
                    if(i+1-j<=m&&y+1<=k) (f[i+1][j][max(x-1,0)][y+1]+=f[i][j][x][y])%=mod;
               }
        for(int i=0;i<=k;i++)
          for(int j=0;j<=k;j++)
            ans=(ans+f[n+m][n][i][j])%mod;
        printf("%d
    ",ans);       
        return 0;
    }
    Code

     

    bzoj2298[HAOI2011]problem a

    题意:一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)

    思路:

    对于每一个描述,我们可以根据他所描述的比他高的和比他矮的人数来构造一条线段,左端点l即为y+1,右端点r为n-x。
    当我们转化成线段以后,这一段线段就表示着分数相同的人数,那么显然,只有与这个线段完全重合的线段是符合要求的,
    对于有交集的线段一定是有一个说谎的,但是对于完全重合的线段,还是有可能出现说谎的情况
    所以我们可以写个前向星add(b[i].r,b[i].l-1,b[i].w)
    l为该区间的左端点,r为该区间的右端点,w为权值
    建这个边的原因是f[b[i].r]=max(f[b[i].r],f[b[j].l-1]+e[i].w)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    #define N 100007
    
    using namespace std;
    int n,tot,num,cnt;
    
    struct node{int l,r,w;}a[N],b[N];
    struct node2{int u,v,net,w;}e[N];
    int f[N],head[N];
    
    int cmp(node a,node b)
    {
        if(a.r==b.r)return a.l<b.l;
        return a.r<b.r;
    }
    int cmp2(node a,node b)
    {
        if(a.r==b.r)return a.w<b.w;
        return a.r<b.r;
    }
    
    void add(int u,int v,int w)
    {
        e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt;
    }
    int main()
    {
        
        scanf("%d",&n);int x,y,ans=0;
        memset(head,-1,sizeof(head)),cnt=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            if(x+y>=n){ans++;continue;}
            a[++tot].l=x+1;
            a[tot].r=n-y;
        }
        sort(a+1,a+tot+1,cmp);
        for(int i=1;i<=tot;i++)
        {
            if(a[i].l==a[i-1].l&&a[i].r==a[i-1].r)
            {
                if(b[num].w<b[num].r-b[num].l+1)
                b[num].w++;
            }
            else b[++num].l=a[i].l,b[num].r=a[i].r,b[num].w=1;
        }
        sort(b+1,b+num+1,cmp2);
        for(int i=1;i<=num;i++) add(b[i].r,b[i].l-1,b[i].w);
        int r=0;
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            f[i]=f[i-1];
            for(int j=head[i];j!=-1;j=e[j].net)
            {
                int v=e[j].v;
                f[i]=max(f[i],f[v]+e[j].w);
            }
        }
        printf("%d
    ",n-f[n]);
    }
    Code

     

     bzoj2431: [HAOI2009]逆序对数列

     题意:若对于任意一个由1~n自然数组成的数列,可以很容易求出有多少个逆序对数。那么逆序对数为k的这样自然数数列到底有多少个?

     思路:

    dp[i][j]表示i的排列中逆序对数为j的方案数
    考虑i的放置,i为最大值,所以放在i-1个位置都可以计算出对答案的贡献
    dp[i][j]=Σdp[i-1][k] (j-i+1 <=k<= j)
    特别的到i时最多可以贡献i-1对逆序对,所以从dp[0]~dp[j-i+1]这一段不能加
    n^3超时,可用前缀和优化
    貌似也可以滚动数组,但蒟蒻不会23333...

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 1001
    #define mod 10000
    
    using namespace std;
    int dp[N][N];
    int n,k,ans,sum;
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) dp[i][0]=1;
        for(int i=2;i<=n;i++)
        {
            sum=0;
            for(int j=0;j<=k;j++)
            {
                (sum+=dp[i-1][j])%mod;
                dp[i][j]=sum%mod;
                if(j-i+1>=0)((sum-=dp[i-1][j-i+1])+=mod)%mod;
            }
        }
        printf("%d
    ",dp[n][k]);
        return 0;
    }
    Code

     

    bzoj4247 挂饰

    题目大意:一开始你有一个挂钩,然后有n个挂饰,每个挂饰有一个价值和挂钩数量,求最大价值。

    思路:

    数据范围有误吧... n<=4000

    dp[i][j] 用完第i个挂饰后还有j个空挂钩的max
    背包问题 挂钩当体积
    按挂钩数量排序 不排序的话这次挂上这个饰品即使j是负数也并不是不合法的,
    因为挂饰间可以互换位置 只要后面挂饰的挂钩能够把j在最后补成自然数就可以了
    dp[i][j]=max(dp[i-1][j],dp[i-1][max(j-a[i].v,0)+1]+a[i].w);

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    #define N 4001
    #define inf 0x3f3f3f3f
    
    using namespace std;
    int dp[N<<1][N];
    int n,ans;
    struct node
    {
        int v,w;
        bool operator < (const node &x) const{
                return v>x.v;
        }
    }a[N];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=0;i<=n;i++)  dp[0][i]=dp[i][n+1]=-inf;
        dp[0][1]=0;
        for(int i=1;i<=n;i++)
          scanf("%d%d",&a[i].v,&a[i].w);
        sort(a+1,a+n+1);
        ans=-inf;
        for(int i=1;i<=n;i++)
          for(int j=0;j<=n;j++)
            dp[i][j]=max(dp[i-1][j],dp[i-1][max(j-a[i].v,0)+1]+a[i].w);
        for(int i=0;i<=n;i++) ans=max(ans,dp[n][i]);
        printf("%d
    ",ans);
        return 0;
    }
    Code

     

    NOI1999 棋盘分割

    题目大意:将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,切n次球均方差最小值

    思路:推一波式子发现答案转化为求平方和的最小值,就可以dp了。

    设f(i,a,b,c,d)表示切第i刀,剩余的矩形左上角和右下角的坐标是(a,b)和(c,d),
    除了剩余部分其它部分的xi平方和的最小值。
    那么f(i)可以向f(i+1)转移,只需要暴力枚举第i+1刀从哪里切了一刀即可。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    const int inf=1<<30;
    int n, chess[9][9],sum[9][9],dp[9][9][9][9][15];
    
    int getX(int y1, int x1, int y2, int x2)
    {
        int a=sum[y2][x2]-sum[y2][x1-1]-sum[y1-1][x2]+sum[y1-1][x1-1];
        return a*a;
    }
    int main()
    {
        scanf("%d", &n);
        for(int i=1; i<=8; i++)
            for(int j=1; j<=8; j++)
                scanf("%d", &chess[i][j]);
        for(int i=1; i<=8; i++)
        {
            for(int j=1; j<=8; j++)
                sum[i][j]=sum[i][j-1]+chess[i][j];
            for(int j=1; j<=8; j++)
                sum[i][j]+=sum[i-1][j];
        }
    
        for(int i1=1; i1<=8; i1++)
          for(int j1=1; j1<=8; j1++)
            for(int i2=i1; i2<=8; i2++)
              for(int j2=j1; j2<=8; j2++)
                dp[i1][j1][i2][j2][0]=getX(i1, j1, i2, j2);
    
        for(int i=1; i<n; i++)
          for(int i1=1; i1<=8; i1++)
            for(int j1=1; j1<=8; j1++)
              for(int i2=i1; i2<=8; i2++)
                for(int j2=j1; j2<=8; j2++)
                {
                    dp[i1][j1][i2][j2][i]=inf;
                    //左右切割
                    for(int k=j1; k<j2; k++)
                      dp[i1][j1][i2][j2][i]=min(dp[i1][j1][i2][j2][i], min(dp[i1][j1][i2][k][i-1]+dp[i1][k+1][i2][j2][0], dp[i1][j1][i2][k][0]+dp[i1][k+1][i2][j2][i-1]));
                    //上下切割
                    for(int k=i1; k<i2; k++)
                      dp[i1][j1][i2][j2][i]=min(dp[i1][j1][i2][j2][i], min(dp[i1][j1][k][j2][i-1]+dp[k+1][j1][i2][j2][0], dp[i1][j1][k][j2][0]+dp[k+1][j1][i2][j2][i-1]));
                }
        printf("%d
    ",dp[1][1][8][8][n-1]);
        return 0;
    }
    Code

     

    Luogu P2339 交作业

    题目大意:要交C门作业,每个办公室有坐标和开放时间。每单位时间内可以向左或向右移动一个单位或者不动,求交完作业并走到某个点的最短时间。

    思路:容易想到把教室排序,一段区间[l,r]先选外侧的教室交作业一定比先选里面的再出来再去另一边更优

    f[l][r][0/1]表示决策到[l,r]这段区间,区间外的都已满足,选则l/r交作业的最短时间,转移看从那个教室移动过来即可。

    #include<bits/stdc++.h>
    
    #define N 1007
    
    using namespace std;
    int n,m,ans,cnt;
    int f[N][N][2];
    struct node{
        int Time,pos;
        bool operator < (const node &a) const{
                return pos<a.pos;
        }
        
    }a[N];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int main()
    {
        int C,H,B;
        C=read();H=read();B=read();
        for(int i=1;i<=C;i++)
          a[i].pos=read(),a[i].Time=read();
        sort(a+1,a+C+1);
        memset(f,127/3,sizeof f);
        f[1][C][0]=max(a[1].Time,a[1].pos);
        f[1][C][1]=max(a[C].Time,a[C].pos);
        
        for(int L=C-2;L>=0;L--) for(int i=1;i+L<=C;++i) 
        {
                int j=i+L;
                f[i][j][0]=min(max(f[i-1][j][0]+a[i].pos-a[i-1].pos,a[i].Time),
                                  max(f[i][j+1][1]+ a[j+1].pos-a[i].pos,a[i].Time));
                f[i][j][1]=min(max(f[i-1][j][0]+a[j].pos - a[i-1].pos,a[j].Time),
                                  max(f[i][j+1][1]+ a[j+1].pos-a[j].pos,a[j].Time));
        }
        ans=0x3f3f3f3f;
        for (int i=1;i<=C;i++) 
            ans=min(ans,f[i][i][0]+abs(a[i].pos-B));
        printf("%d
    ",ans);
        return 0;
    }
    Code

     

    bzoj2101[Usaco2010 Dec]Treasure Chest 藏宝箱

    题目大意:一排金币两个人轮流取,每个人取最左边或最右边,问先手获得的最大值。

    思路:显然先手要让对手得到的最小,用区间的和减去对手的最小值就是先手的最大值。

    f[i][j]=sum[j]-sum[i-1]-min(f[i+1][j],f[i][j+1])。空间不够,滚动数组优化。

    #include<bits/stdc++.h>
    
    #define N 5001
    
    using namespace std;
    int n,m,k,ans,cnt;
    int f[2][N],sum[N];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int main()
    {
        freopen("ly.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++)
        {
            sum[i]=read();
            f[k][i]=sum[i];sum[i]+=sum[i-1];
        }
        for(int L=2;L<=n;L++) 
        {
            for(int i=1;i+L-1<=n;i++)
            {
                int j=i+L-1;
                f[k^1][i]=sum[j]-sum[i-1]-min(f[k][i],f[k][i+1]);
            }k^=1;
        }
        printf("%d
    ",f[k][1]);
        return 0;
    }
    Code

     

    bzoj 1617: [Usaco2008 Mar]River Crossing渡河问题

    思路:f[i]表示运i头牛的最短时间。转移枚举最后一次运几头。

    f[i]=min(f[i],f[j]+sum[i-j]+m);

    #include<bits/stdc++.h>
    
    #define N 2511
    #define ll long long
    #define inf 0x3f3f3f3f;
    
    using namespace std;
    int n,m,cnt;
    ll ans,f[N],T[N],sum[N]; 
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int main()
    {
        n=read();m=read();sum[0]=m;
        for(int i=1;i<=n;i++) f[i]=inf;
        for(int i=1;i<=n;i++)
        {
            T[i]=read();sum[i]=sum[i-1]+T[i];
            f[i]=sum[i]+m;
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                f[i]=min(f[i],f[j]+sum[i-j]+m);
            }
        }
        printf("%lld
    ",f[n]-m);
        return 0;
    } 
    Code

     

    bzoj4580: [Usaco2016 Open]248

    思路:f[i][j]表示到第i个数,得到数值为j,向右合并的最右端点。 方程f[i][j]=f[f[i][j-1]][j-1];类似倍增。

    #include<bits/stdc++.h>
    
    #define N 270000
    
    using namespace std;
    int n,m,ans;
    int f[N][66],a[N];
    int main()
    {
        freopen("ly.in","r",stdin);
        scanf("%d",&n);
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            f[i][a[i]]=i+1;
        }
        for (int j=2; j<=60; j++)
            for (int i=1; i<=n; i++)
            {
                if (f[i][j]==0) f[i][j]=f[f[i][j-1]][j-1];
                if (f[i][j]>0) ans=max(ans,j);
            }
        printf("%d
    ",ans);
        return 0;
    }
    Code

    bzoj4758: [Usaco2017 Jan]Subsequence Reversal(区间dp)

    思路:想到可能是道区间dp,emm那就考虑一段区间[l,r]怎么维护里面交换那些数呢?

    发现可以用值域这个东西把数给框住。又,反转区间肯定是越靠右的反转到越靠左位置。

    那么由小区间推大区间时,只需要判断端点处包不包括在这一次的交换中即可。
    所以可dp[i][j][L][R]为区间[i,j]里面min(ak) >= L, max(ak) <= R时,反转一次的最长不下降子序列。
    转移见代码。

    #include<bits/stdc++.h>
    
    #define N 51
    
    using namespace std;
    int n,a[N],ans;
    int dp[N][N][N][N];
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int main()
    {
      n=read();
      for(int i=1; i <= n; ++i) a[i]=read(),dp[i][i][a[i]][a[i]]=1;
      
      for(int len=2; len <= n; ++len) for(int i=1; i+len-1 <= n; ++i)//当前区间 
          {
                   int j=i+len-1;
                for(int l=1; l <= 50; ++l) for(int L=1; L+l-1 <= 50; ++L)//当前值域 
                {
                      int R=L+l-1;
                      ans=dp[i][j][L][R];
                      ans=max(ans,max(dp[i+1][j][L][R],dp[i][j-1][L][R]));
                      ans=max(ans,max(dp[i][j][L+1][R],dp[i][j][L][R-1]));
                      dp[i][j][L][R]=ans;
                      //copy小区间的答案 
                      dp[i][j][min(L,a[i])][R]=max(dp[i][j][min(L,a[i])][R],dp[i+1][j][L][R]+(a[i] <= L));
                      dp[i][j][L][max(R,a[j])]=max(dp[i][j][L][max(R,a[j])],dp[i][j-1][L][R]+(a[j] >= R));
                      dp[i][j][min(L,a[i])][max(R,a[j])]=max(dp[i][j][min(L,a[i])][max(R,a[j])],dp[i+1][j-1][L][R]+(a[j] >= R)+(a[i] <= L));
                      //a[i]与a[j]不交换 
                      dp[i][j][min(L,a[j])][R]=max(dp[i][j][min(L,a[j])][R],dp[i+1][j-1][L][R]+(a[j] <= L));  
                      dp[i][j][L][max(R,a[i])]=max(dp[i][j][L][max(R,a[i])],dp[i+1][j-1][L][R]+(a[i] >= R));
                      dp[i][j][min(L,a[j])][max(R,a[i])]=max(dp[i][j][min(L,a[j])][max(R,a[i])],dp[i+1][j-1][L][R]+(a[i] >= R)+(a[j] <= L));
                       //a[i]与a[j]交换 
                }
          }
      cout<<dp[1][n][1][50]<<endl;
      return 0;
    }
    Code
  • 相关阅读:
    LeetCode 123. Best Time to Buy and Sell Stock III (stock problem)
    精帖转载(关于stock problem)
    LeetCode 122. Best Time to Buy and Sell Stock II (stock problem)
    LeetCode 121. Best Time to Buy and Sell Stock (stock problem)
    LeetCode 120. Triangle
    基于docker 搭建Elasticsearch5.6.4 分布式集群
    从零开始构建一个centos+jdk7+tomcat7的docker镜像文件
    Harbor实现容器镜像仓库的管理和运维
    docker中制作自己的JDK+tomcat镜像
    docker镜像制作---jdk7+tomcat7基础镜像
  • 原文地址:https://www.cnblogs.com/L-Memory/p/7337513.html
Copyright © 2011-2022 走看看