精华内容
下载资源
问答
  • 今天我们聊的话题是“股票放量一定是好事么,为什么从爆大量的高位开始下跌”? 我们去翻一下每一个股票,都会发现股价最高点基本上都是伴随着最大成交量的放出,有的成交量连续放出好几日,然后创出天价,有的是...

    各位投资朋友大家好,欢迎收听《抢财猫股票课堂》,我是你们的老朋友波哥。

     

    今天我们聊的话题是“股票放量一定是好事么,为什么从爆大量的高位开始下跌”?

    我们去翻一下每一个股票,都会发现股价最高点基本上都是伴随着最大成交量的放出,有的成交量连续放出好几日,然后创出天价,有的是随后几日缩量创出天价,那么我们去分析这种现象背后的原因是什么。

     

    先想想为什么放量股票才会涨?就是有更多的人买,每个人都会觉得买了之后第二天会有更多人进场抢筹码,没人说自己买了第二天就会跌,那他肯定不会去买;但是一个股票从低估到回归正常区间波动,到过热,这个过程就是投资者集体非理性的表现,表现在越涨越觉得还能涨,那么上涨趋势一般而言会有一个惯性,还能涨,但是明显动能不足,意味着主力开始派发筹码,开始有人获利了结,后续进场的人如果没有大资金的解盘仅仅靠散户的小资金买入,是扛不住多久的;

     

    那么在看爆大量的价格基本上就叫做天量和天价,就是在一个很长的时间周期里都没出出现过这么大的成交量了,也没有出现这么高的价格,这背后的利多消息,促使市场做多情绪非常高涨,在指标上可以明显看出进入超买区域,这个时候也是最危险最好赚钱的机会,最危险也就是说随时股价都可能掉头下来,最好赚钱机会针对大资金的游资而言,资金流动性好很容易进出,对于他们而言能够很清楚分辨出行业是不是已经到了终结出局的地步了,如果到了就会严格执行操作。

     

    那么重点来了,如何能够在股价一步步创新高保持安全的仓位,保持一颗警惕的心,如何能够判断出不该买的买点,不被追涨在放大量的最高点,这就需要今后在培训课程里会去深入讲解。

     

    本期节目内容讲到这,希望投资朋友们有问题留言与我交流,也希望大家转发分享给更多的朋友,关注微信公共号抢财猫,谢谢,再见。

     

    完整 音频 收听 , 点击:

    喜马拉雅​www.ximalaya.com

    打开喜马拉雅 FM手机APP,搜索并关注:抢财猫股票课堂,可以完整看到全部课程 节目

    展开全文
  • 区间dp入门

    万次阅读 多人点赞 2018-05-03 18:07:49
    顾名思义:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。二.核心思路既然让我求解在一个区间上的最优解,那么我把这个区间分割成一...

    一.什么是区间dp?

    顾名思义:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。

    二.核心思路

    既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,求解每个小区间的最优解,再合并小区间得到大区间即可。所以在代码实现上,我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),内层枚举该长度下可以的起点,自然终点也就明了了。然后在这个起点终点之间枚举分割点,求解这段小区间在某个分割点下的最优解。

    板子:

    for(int len = 1;len<=n;len++){//枚举长度
            for(int j = 1;j+len<=n+1;j++){//枚举起点,ends<=n
                int ends = j+len - 1;
                for(int i = j;i<ends;i++){//枚举分割点,更新小区间最优解
                    dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends]+something);
                }
            }
        }

    三.朴素区间dp(n^3)

    例题:石子归并1

    传送门:戳我呀

    转移方程:

    dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends]+weigth[i][ends]);

    j~ends堆合并 = 较小的(原来, 分割点i坐部分重量 + 分割点i右边部分重量 + 合并后两堆总重量)

    注:可以用sum[j] - sum[i - 1]表示i~j堆的重量!

    代码:

    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define INF 0x3f3f3f
    int stone[105];
    int dp[105][105];
    int sum[105];
    int main()
    {
        int n;
        scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        memset(dp,INF,sizeof(dp));
        for(int  i =1;i<=n;i++){
            scanf("%d",&stone[i]);
            sum[i] = sum[i - 1] + stone[i];//重量
            dp[i][i] = 0;
        }
        for(int len = 1;len<=n;len++){//枚举长度
            for(int j = 1;j+len<=n+1;j++){//枚举起点,ends<=n
                int ends = j+len - 1;
                for(int i = j;i<ends;i++){//枚举分割点
                    dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends]+sum[ends]-sum[j-1]);//更新状态
                }
            }
        }
        cout<<dp[1][n]<<endl;
        return 0;
    }
    

    四.题目变形(线性变环状)

    例题:石子归并2

    题意:原题与上面相同,但是石子排列由线性排列变成环状排列,求解

    思路:环状以后合并区间的情况就可以从后往前合并,最后合并完成可能是1~n,2~n~1,3~n~2.....这种n个石子合并的情况。所以我们可以破环成链,将前n-1各元素也放到n后面构成一个线性的环状序列,在对这个序列dp即可

                                        

     

    代码:codevs 2102环状石子归并求最大值和最小值

    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define INF 0x3f3f3f
    int stone[105];
    int dpmin[205][205];//最小
    int dpmax[205][205];//最大
    int sum[205];
    int main()
    {
        int n;
        scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        memset(dpmin,INF,sizeof(dpmin));
        memset(dpmax,-1,sizeof(dpmax));
        for(int  i =1;i<=n;i++){
            scanf("%d",&stone[i]);
            sum[i] = sum[i - 1] + stone[i];
            dpmin[i][i] = 0;
            dpmax[i][i] = 0;
        }
        for(int i = 1;i<=n;i++){
            sum[i+n] = sum[i+n-1]+stone[i];//展开的n后面的n-1~1重量
            dpmin[i+n][i+n] = 0;
            dpmax[i+n][i+n] = 0;
        }
        for(int len = 1;len<=n;len++){//长度还是最大n
            for(int j = 1;j+len<=2*n;j++){//起点枚举最大到2*n-1,ends<=2*n-1
                int ends = j+len - 1;
                for(int i = j;i<ends;i++){//注意!i<ends!!!因为i=ends时,dp[ends+1][ends]是不成立的!
                    dpmin[j][ends] = min(dpmin[j][ends],dpmin[j][i]+dpmin[i+1][ends]+sum[ends]-sum[j-1]);
                    dpmax[j][ends] = max(dpmax[j][ends],dpmax[j][i]+dpmax[i+1][ends]+sum[ends]-sum[j-1]);
                }
            }
        }
        int ansmin = 0xfffffff;
        int ansmax = -1;
        for(int i = 1;i<=n;i++){
            ansmin = min(ansmin,dpmin[i][i+n-1]);//找1~n,2~n~1,3~n~2....的合并n个堆的中最大和最小的值
            ansmax = max(ansmax,dpmax[i][i+n-1]);
        }
        cout<<ansmin<<endl;
        cout<<ansmax<<endl;
        return 0;
    }
    

    五.时间优化(四边形优化,n^2)

    思路:在查找最优分割点的时候,我们浪费了大量时间。那么我们可以把最优分割点保存下来,在查找的时候利用保存的最优分割点来优化查找过程。

    四边形不等式优化:

    (1)功能:用来寻找,s[i][j](i~j的最优分割点)与其他分割点的关系

    (2)不等式内容:如果某东西满足a<b<=c<d且f[a][c]+f[b][d]<=f[a][d]+f[b][c],则说这个东西满足四边形不等式。简而言之:交叉小于包含!

    (3)结论关系:s[i][j-1]<=s[i][j]<=s[i+1][j] 

    (4)证明过程:

     0.证明w满足四边形不等式,这里w是m的附属量,形如m[i,j]=opt{m[i,k]+m[k,j]+w[i,j]},此时大多要先证明w满足条件才能进一步证明m满足条件

     1.证明m满足四边形不等式

     2.证明s[i,j-1]≤s[i,j]≤s[i+1,j]

    (5)证明请参考:点击打开链接

    代码:(以石子归并v2为例)

    #include <iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define INF 0x3f3f3f
    int dp[2005][2005];
    int sum[2005];
    int relation[2005][2005];
    int num[2005];
    int main()
    {
        int n;
        scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        memset(dp,0x3f,sizeof(dp));
        for(int i = 1;i<=n;i++){
            scanf("%d",&num[i]);
            dp[i][i] = 0;
            relation[i][i] = i;
            sum[i] = sum[i-1] + num[i];
        }
        for(int i = 1;i<=n;i++){
            sum[i+n] = sum[i+n-1] +num[i];
            relation[i+n][i+n] = i+n;//分割点初始化
            dp[i+n][i+n] = 0;
        }
        for(int len = 1;len<=n;len++){
            for(int j = 1;j+len<=2*n;j++){
                int ends = j+len - 1;
                for(int k = relation[j][ends-1];k<=relation[j+1][ends];k++){//k的范围
                    if(dp[j][ends]>dp[j][k]+dp[k+1][ends]+sum[ends]-sum[j-1])
                    {
                        dp[j][ends]=dp[j][k]+dp[k+1][ends]+sum[ends]-sum[j-1];
                        relation[j][ends] = k;
                    }
                }
            }
        }
        int ans = 0xfffffff;//一定要开0xfffffff不然错QAQ
        for(int i = 1;i<=n;i++){
            ans = min(ans,dp[i][i+n-1]);
        }
        printf("%d\n",ans);
        return 0;
    }
    

    六.四道入门题目(难点在于怎么找状态转移方程)

    1.Poj2955 括号匹配(一)

    (1)题意:给出一个的只有'(',')','[',']'四种括号组成的字符串,求 最多 有多少个括号满足题目里所描述的完全匹配。

    (2)思路:这里的状态转移是以一个if为基础的,如果s[i]与s[j]匹配,那么明显的dp[i][j] = dp[i+1][j-1]+2;然后在这个基础上枚举分割点k.

    (3)状态转移方程:dp[i][j]表示第i~j个字符间的最大匹配字符数。

    if(s[i] 与 s[j]匹配) dp[i][j] = d[[i+1][j-1] +2;

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

    代码:

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int dp[105][105];
    int main()
    {
        char s[105];
        while(scanf("%s",s+1)!=EOF)
        {
            memset(dp,0,sizeof(dp));//dp初始化为0,因为一方面是找最大之,一方面初始匹配数为0
            int len = strlen(s+1);//dp[i][i]不用处理,因为自己和自己不匹配就是0
            if(s[1]=='e')break;
            for(int l = 1;l<=len;l++){
                for(int i = 1;i+l<=len+1;i++){
                    int j= i+l-1;
                    if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')){//如果匹配,先更新
                        dp[i][j] = dp[i+1][j-1]+2;
                    }
                    for(int k = i;k<j;k++){//k<j
                        dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);
                    }
                }
            }
            cout<<dp[1][len]<<endl;
        }
        return 0;
    }
    

    2.Poj1651 抽卡片

    (1)题意:给你n个数字,要求不能删除两端点的数字,然后删除其他数字的代价是该数字和左右相邻数字的乘积,问把数字(除端点)删完后的最小总代价。

    (2)思路:因为最后都要删掉中间所有的数字,所以我们分隔一个个小区间删数字,合并区间求最小。那么我们的状态就是目前删掉的数字区间,但是我们分割的时候的意思是抽一个卡片出来,所以这个卡片不能在已经抽出的状态里面,所以dp[i][j]里面是不包含j卡片的!

    (3)状态转移方程:dp[i][j]表示抽出第i~j-1张卡片时候的最小值

    dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] +num[i-1]*num[k]*num[j]);

    代码:

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int dp[105][105];
    int num[105];
    int main()
    {
        int N;
        cin>>N;
        memset(dp,0x3f,sizeof(dp));//dp初始化为inf,因为找最小值
        for(int i = 1;i<=N;i++){
            cin>>num[i];
            dp[i][i] = 0;//dp[i][i]要初始化,作为由小到大累计的初始条件,自己取出自己就是为0
        }
        for(int len = 1;len<=N;len++){
            for(int i = 2;i+len<=N+1;i++){//起点从2开始,因为不包括两端点
                int j = i+len-1;
                for(int k = i;k<j;k++){
                    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+num[i-1]*num[k]*num[j]);
                }
            }
        }
        cout<<dp[2][N]<<endl;//由dp[i][j]的定义,输出合并第2~N-1个卡片的结果
        return 0;
    }
    

    3.整数划分(四)

    (1)题意:给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积

    (2)思路:这里给的乘号是有限个,所以状态方程里必须包含使用乘号的个数,此外还要包含区间长度。所以怎么用二维dp实现包含m和n,我们可以用dp[i][j]表示在第1~i个字符里插入j个乘号的最大值。

    (3)状态转移方程 dp[i][j]表示在第1~i个字符里插入j个乘号的最大值;用num[i][j]表示第i~j个字符表示的数字;

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

    代码:

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll dp[50][50];
    ll num[50][50];
    int main()
    {
        int T;
        scanf("%d",&T);
        char s[100];
        while(T--)
        {
            int m;
            scanf("%s%d",s+1,&m);
            int len = strlen(s+1);
            memset(dp,0,sizeof(dp));//初始化dp
            memset(num,0,sizeof(num));
            for(int i = 1; i<=len; i++)
            {
    
                for(int j = i; j<=len; j++)
                {
                    for(int k = i;k<=j;k++){
                        num[i][j]*=10;
                        num[i][j]+=(s[k]-'0');
                    }
                }
                dp[i][0] = num[1][i];//初始插入0个乘号时是自己,由小到大累计的基础
            }
            for(int j = 1;j<m;j++){//乘号个数由1~m-1个
                for(int i = 1;i<=len;i++){//结尾坐标(表示长度)
                    for(int k = 1;k<i;k++){//分割点插入第j个乘号
                        dp[i][j] = max(dp[i][j],dp[k][j-1]*num[k+1][i]);
                    }
                }
            }
            cout<<dp[len][m-1]<<endl;//输出在1~len插入m-1个乘号的结果
        }
        return 0;
    }
    

    4.最多回文子串 hdu4632

    (1)题意:给你一个字符串,求出其最多的可构成的回文字串(不要求连续),注:这里不同的回文字串只要求位置不同即可视为不同,如:aaaaa 的最多回文子串数目是 31.

    (2)思路:用dp[i][j]表示状态,表示i~j里最多的回文字串数目,假设现在我们要求dp[i][j]:

    a.首先:由前一个状态知:dp[i][j] = dp[i+1][j]并上dp[i][j-1] (因为区间尽可能大而且状态要在dp[i][j]之前,而且回文子串不要求 连续),由容斥原理得:dp[i+1][j] U dp[i][j-1] = dp[i+1][j]+dp[i][j-1] - dp[i+1][j] n dp[i][j-1]

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

    注意:这是一个固定的状态,每一个状态都由这个公式推出初始状态,是必须的,不是可选择地

    b.其次:如果s[i] == s[j] ,那么两端单独就可以构成回文子序列,而且与dp[i+1][j],dp[i][j-1],dp[i+1][j-1],中的回文序列又可以构成新的回文序列,所以此时dp[i][j] = dp[i+1][j] U dp[i][j-1] + dp[i+1][j-1] +1;而dp[i][j]已经更新为 dp[i+1][j] U dp[i][j-1],所以dp[i][j] = dp[i][j] + dp[i+1][j-1] +1;

    (3)状态转移方程: dp[i][j]表示i~j内最多的回文字串数目

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

    if(s[i] == s[j]) dp[i][j] = dp[i][j] +dp[i+1][j-1] +1; (思维)

    注:这里因为容斥时有减法,所以要先加上模再取模,要不会出

    现负数!

    代码:

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    char s[1005];
    int dp[1005][1005];
    int main()
    {
        int T;
        scanf("%d",&T);
        int t = 0;
        while(T--){
            t++;
            scanf("%s",s+1);
            memset(dp,0,sizeof(dp));
            int len = strlen(s+1);
            for(int i = 1;i<=len;i++)dp[i][i] = 1;//自己是长度为1的
            for(int l = 1;l<=len;l++){
                for(int i = 1;i+l<=len+1;i++){
                    int j = i+l-1;//容斥原理 dp[i][j] = dp[i+1][j]并dp[i][j-1] (因为是自序列可以不连续)
                    dp[i][j] = (dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]+10007)%10007;
                    if(s[i] == s[j])dp[i][j]=(dp[i][j]+dp[i+1][j-1] + 1)%10007;//如果两端相等,dp[i][j] = 原来的 + 两端与中间每一个回文也可以构成回文(dp[i+1][j-1]) + 两端单独构成一个回文(1)!!!
                }
            }
            printf("Case %d: %d\n",t,dp[1][len]);
    
        }
        return 0;
    }
    

     

    展开全文
  • Splay tree 区间翻转 模板

    千次阅读 2014-09-06 20:19:50
    Splay作为二叉平衡树与其他...一个模板,只支持区间翻转。想看其他操作的可以看我的 维修数列 的博客:http://blog.csdn.net/jiangyuze831/article/details/39098481 PS:还是1A的,有点小开心…… CODE:

    Splay作为二叉平衡树与其他二叉平衡树不同的是,Splay能够支持区间操作。最然可持续化Treap也可以做到,但是代码量实在是难以同日而语。

    放一个模板,只支持区间翻转。想看其他操作的可以看我的 维修数列 的博客:http://blog.csdn.net/jiangyuze831/article/details/39098481


    PS:还是1A的,有点小开心……


    CODE:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    struct Complex{
    	int val,size;
    	bool reverse;
    	Complex *son[2],*father;
    
    	bool Check() {
    		return father->son[1] == this;
    	}
    	void Combine(Complex *a,bool dir) {
    		son[dir] = a;
    		a->father = this;
    	}
    	void Reverse() {
            reverse ^= 1;
    		swap(son[0],son[1]);
    	}
    }none,*nil = &none,*root = nil;
    
    int cnt,asks;
    int last;
    
    Complex *BuildTree(int l,int r);
    Complex *NewComplex(Complex *f,int x);
    Complex *FindK(Complex *a,int k);
    inline void Rotate(Complex *a,bool dir);
    inline void Splay(Complex *a,Complex *aim);
    inline void Work(int x,int y);
    inline void PushUp(Complex *a);
    inline void PushDown(Complex *a);
    void Print(Complex *a);
    
    int main()
    {
    	cin >> cnt >> asks;
    	root = BuildTree(0,cnt + 1);
    	root->father = nil;
    	for(int x,y,i = 1;i <= asks; ++i) {
            scanf("%d%d",&x,&y);
    		Work(x,y);
    	}
        last = FindK(root,cnt + 1)->val;
    	Print(root);
    	return 0;
    }
    
    Complex *NewComplex(Complex *f,int val)
    {
        Complex *re = new Complex();
        re->father = f;
        re->val = val;
        re->son[0] = re->son[1] = nil;
        re->reverse = false;
        return re;
    }
    
    Complex *BuildTree(int l,int r)
    {
    	if(l > r)	return nil;
    	int mid = (l + r) >> 1;
    	Complex *re = NewComplex(re,mid);
    	re->Combine(BuildTree(l,mid - 1),false);
    	re->Combine(BuildTree(mid + 1,r),true);
    	PushUp(re);
    	return re;
    }
    
    Complex *FindK(Complex *a,int k)
    {
    	PushDown(a);
    	if(k <= a->son[0]->size)	return FindK(a->son[0],k);
    	k -= a->son[0]->size;
    	if(k == 1)	return a;
    	return FindK(a->son[1],k - 1);
    }
    
    inline void Rotate(Complex *a,bool dir)
    {
    	Complex *f = a->father;
    	PushDown(f),PushDown(a);
    	f->son[!dir] = a->son[dir];
    	f->son[!dir]->father = f;
    	a->son[dir] = f;
    	a->father = f->father;
    	f->father->son[f->Check()] = a;
    	f->father = a;
    	PushUp(f);
        if(root == f)   root = a;
    }
    
    inline void Splay(Complex *a,Complex *aim)
    {
    	while(a->father != aim) {
    		if(a->father->father == aim) 
    			Rotate(a,!a->Check());
    		else if(!a->father->Check()) {
    			if(!a->Check()) {
    				Rotate(a->father,true);
    				Rotate(a,true);
    			}
    			else {
    				Rotate(a,false);
    				Rotate(a,true);
    			}
    		}
    		else {
    			if(a->Check()) {
    				Rotate(a->father,false);
    				Rotate(a,false);
    			}
    			else {
    				Rotate(a,true);
    				Rotate(a,false);
    			}
    		}
    	}
    	PushUp(a);
    }
    
    inline void Work(int x,int y)
    {
    	x++,y++;
    	Splay(FindK(root,x - 1),nil);
    	Splay(FindK(root,y + 1),root);
    	root->son[1]->son[0]->Reverse();
    }
    
    inline void PushUp(Complex *a)
    {
        if(a == nil)    return ;
        a->size = a->son[0]->size + a->son[1]->size + 1;
    }
    
    inline void PushDown(Complex *a)
    {
        if(a->reverse) {
            a->son[0]->Reverse();
            a->son[1]->Reverse();
            a->reverse = false;
        }
    }
    
    void Print(Complex *a)
    {
    	if(a == nil)	return ;
        PushDown(a);
    	Print(a->son[0]);
        if(a->val && a->val != cnt + 1) {
            printf("%d",a->val);
            if(a->val != last)  putchar(' ');
        }
    	Print(a->son[1]);
    }


    展开全文
  • 区间DP小结(附经典例题)

    万次阅读 多人点赞 2017-08-13 12:50:24
    ——这篇文章主要想总结下区间DP的经典题目,同时给自己复习巩固这方面知识点。 区间DP 一、定义 区间DP,顾名思义是在区间上DP,它的主要思想就是先在小区间进行DP得到最优解,然后再利用小区间的最优解...

    ——这篇文章主要想总结下区间DP的经典题目,同时给自己复习巩固这方面知识点。

     

    区间DP

     

    一、定义

     

    区间DP,顾名思义是在区间上DP,它的主要思想就是先在小区间进行DP得到最优解,然后再利用小区间的最优解合并求大区间的最优解。

     

     

    二、实现思路

     

    下面给出区间DP最简单形式的伪代码(具体要根据题目修改)

     

    //mst(dp,0) 初始化DP数组
    for(int i=1;i<=n;i++)
    {
        dp[i][i]=初始值
    }
    for(int len=2;len<=n;len++)  //区间长度
    for(int i=1;i<=n;i++)        //枚举起点
    {
        int j=i+len-1;           //区间终点
        if(j>n) break;           //越界结束
        for(int k=i;k<j;k++)     //枚举分割点,构造状态转移方程
        {
            dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
        }
    }

     

     

    三、经典例题

     

    1. 石子合并问题

     

    题目链接

    石子合并(一)

    时间限制:1000 ms  |  内存限制:65535 KB

    难度:3

    描述 

    N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

    输入

    有多组测试数据,输入到文件结束。
    每组测试数据第一行有一个整数n,表示有n堆石子。
    接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

    输出

    输出总代价的最小值,占单独的一行

    样例输入

     

    3

    1 2 3

    7

    13 7 8 16 21 4 18

    样例输出

     

    9

    239

     

     

     

    【思路】

     

    我们dp[i][j]来表示合并第i堆到第j堆石子的最小代价。

     

    那么状态转移方程为

     

    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);

     

    其中w[i][j]表示把两部分合并起来的代价,即从第i堆到第j堆石子个数的和,为了方便查询,我们可以用sum[i]表示从第1堆到第i堆的石子个数和,那么w[i][j]=sum[j]-sum[i-1].

     

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 205;
    const ll mod = 1e9+7;
    const ll INF = 1e18;
    const double eps = 1e-9;
    
    int n,x;
    int sum[maxn];
    int dp[maxn][maxn];
    
    int main()
    {
        while(~scanf("%d",&n))
        {
            sum[0]=0;
            mst(dp,0x3f);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&x);
                sum[i]=sum[i-1]+x;
                dp[i][i]=0;
            }
            for(int len=2;len<=n;len++)
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) continue;
                for(int k=i;k<j;k++)
                {
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
                }
            }
            printf("%d\n",dp[1][n]);
        }
        return 0;
    }
    

     

    【平行四边形优化】

     

    上面的代码运行时间在240ms左右,通过这题完全没问题,但我们还可以考虑优化。

     

    由于状态转移时是三重循环的,我们想能否把其中一层优化呢?尤其是枚举分割点的那个,显然我们用了大量的时间去寻找这个最优分割点,所以我们考虑把这个点找到后保存下来

     

    用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】。(这个时候小区间s[i][j-1]和s[i+1][j]的值已经求出来了,然后通过这个循环又可以得到s[i][j]的值)。

     

    关于平行四边形优化的证明可以参考这篇博客: 戳我戳我

     

    【32ms】

     

     

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 205;
    const ll mod = 1e9+7;
    const ll INF = 1e18;
    const double eps = 1e-9;
    
    int n,x;
    int sum[maxn];
    int dp[maxn][maxn];
    int s[maxn][maxn];
    
    int main()
    {
        while(~scanf("%d",&n))
        {
            sum[0]=0;
            mst(dp,0x3f);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&x);
                sum[i]=sum[i-1]+x;
                dp[i][i]=0;
                s[i][i]=i;
            }
            for(int len=2;len<=n;len++)
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) continue;
                for(int k=s[i][j-1];k<=s[i+1][j];k++)
                {
                    if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j])
                    {
                        dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                        s[i][j]=k;
                    }
                }
            }
            printf("%d\n",dp[1][n]);
        }
        return 0;
    }
    

     

    附:此题的升级版  HDOJ 3506 Monkey Party

    Monkey Party

    Time Limit: 4000/2000 MS (Java/Others)    Memory Limit:131072/65536 K (Java/Others)
    Total Submission(s): 1738    Accepted Submission(s): 784

     

    Problem Description

    Far away from our world, there is a banana forest. And many lovely monkeys livethere. One day, SDH(Song Da Hou), who is the king of banana forest, decides tohold a big party to celebrate Crazy Bananas Day. But the little monkeys don'tknow each other, so as the king, SDH must do something.
    Now there are n monkeys sitting in a circle, and each monkey has a makingfriends time. Also, each monkey has two neighbor. SDH wants to introduce themto each other, and the rules are:
    1.every time, he can only introduce one monkey and one of this monkey'sneighbor.
    2.if he introduce A and B, then every monkey A already knows will know everymonkey B already knows, and the total time for this introducing is the sum ofthe making friends time of all the monkeys A and B already knows;
    3.each little monkey knows himself;
    In order to begin the party and eat bananas as soon as possible, SDH want toknow the mininal time he needs on introducing.

     

     

    Input

    There is several test cases. In each case, the first line is n(1 ≤ n ≤ 1000), whichis the number of monkeys. The next line contains n positive integers(less than1000), means the making friends time(in order, the first one and the last oneare neighbors). The input is end of file.

     

     

    Output

    For each case, you should print a line giving the mininal time SDH needs on introducing.

     

     

    Sample Input

    8

    5 2 4 7 6 1 3 9

     

     

    Sample Output

    105

     

     


    【题意】

     

    问题转化后其实就是环形石子合并,即现在有围成一圈的若干堆石子,其他条件跟其那面那题相同,问合并所需最小代价。

     

    【思路】

     

    我们需要做的是尽量向简单的问题转化,可以把前n-1堆石子一个个移到第n个后面,那样环就变成了线,即现在有2*n-1堆石子需要合并,我们只要求下面的式子即可。求法与上面那题完全一样。

     

    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 1005*2;
    const ll mod = 1e9+7;
    const int INF = 0x3f3f3f3f;
    const double eps = 1e-9;
    
    int a[maxn];
    int sum[maxn];
    int dp[maxn][maxn];
    int s[maxn][maxn];
    
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            sum[0]=0;
            mst(dp,0x3f);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                sum[i]=sum[i-1]+a[i];
                s[i][i]=i;
                dp[i][i]=0;
            }
            for(int i=1;i<n;i++)
            {
                sum[i+n]=sum[i+n-1]+a[i];
                s[i+n][i+n]=i+n;
                dp[i+n][i+n]=0;
            }
            for(int len=2;len<=n;len++)
            for(int i=1;i<=2*n-1;i++)
            {
                int j=i+len-1;
                if(j>2*n-1) break;
                for(int k=s[i][j-1];k<=s[i+1][j];k++)
                {
                    if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
                    {
                        dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                        s[i][j]=k;
                    }
                }
            }
            int ans=INF;
            for(int i=1;i<=n;i++)
            {
                ans=min(ans,dp[i][i+n-1]);
            }
            printf("%d\n",ans);
        }
        return 0;
    }

     

     

     

     

     


    2. 括号匹配问题

     

     

    题目链接

     

    Brackets

     

    Time Limit: 1000MS

    Memory Limit: 65536K

    Total Submissions: 8710

    Accepted: 4659

    Description

    We give the following inductive definition of a “regular brackets” sequence:

    • the empty sequence is a regular brackets sequence,
    • if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
    • if a andb are regular brackets sequences, thenab is a regular brackets sequence.
    • no other sequence is a regular brackets sequence

    For instance,all of the following character sequences are regular brackets sequences:

    (), [], (()), ()[], ()[()]

    while thefollowing character sequences are not:

    (, ], )(, ([)], ([(]

    Given a brackets sequence of characters a1a2 … an,your goal is to find the length of the longest regular brackets sequence that is a subsequence ofs. That is, you wish to find the largestmsuch that for indicesi1,i2, …,imwhere 1 ≤i1 <i2 < … <imn,ai1ai2 … aim is a regular bracketssequence.

    Given the initial sequence ([([]])], the longest regularbrackets subsequence is[([])].

    Input

    The input test file will contain multiple test cases. Each input test case consists of asingle line containing only the characters(,),[, and]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.

    Output

    For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.

    Sample Input

    ((()))

    ()()()

    ([]])

    )[)(

    ([][][)

    end

    Sample Output

    6

    6

    4

    0

    6

     

    【题意】

     

    给出一个的只有'(',')','[',']'四种括号组成的字符串,求最多有多少个括号满足题目里所描述的完全匹配。

     

    【思路】

     

    用dp[i][j]表示区间[i,j]里最大完全匹配数。

     

    只要得到了dp[i][j],那么就可以得到dp[i-1][j+1]

     

    dp[i-1][j+1]=dp[i][j]+(s[i-1]与[j+1]匹配 ? 2 : 0)

     

    然后利用状态转移方程更新一下区间最优解即可。

     

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

     

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 105;
    const ll mod = 1e9+7;
    const ll INF = 1e18;
    const double eps = 1e-9;
    
    char s[maxn];
    int dp[maxn][maxn];
    
    int main()
    {
        while(~scanf("%s",s+1)&&s[1]!='e')
        {
            int n=strlen(s+1);
            mst(dp,0);
            for(int len=2;len<=n;len++)
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                if(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')
                {
                    dp[i][j]=dp[i+1][j-1]+2;
                }
                for(int k=i;k<j;k++)
                {
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]);
                }
            }
            printf("%d\n",dp[1][n]);
        }
        return 0;
    }
    

     

     

    【变题】 题目链接

     

    括号匹配(二)

    时间限制:1000 ms  |  内存限制:65535 KB

    难度:6

    描述

    给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
    如:
    []是匹配的
    ([])[]是匹配的
    ((]是不匹配的
    ([)]是不匹配的

    输入

    第一行输入一个正整数N,表示测试数据组数(N<=10)
    每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100

    输出

    对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行

    样例输入

     

    4

    []

    ([])[]

    ((]

    ([)]

    样例输出

     

    0

    0

    3

    2

     

    【题意】

     

    上一题求的是满足完美匹配的最大括号数量,而这题问的是使所有括号完美匹配需要添加的最小括号数量

     

    【思路】

     

    显然,要使添加的括号尽量少,我们需要使原来的括号序列尽可能多得匹配,即先求最大匹配数量(跟上题一样),那么还剩下一些没有匹配的括号,我们就需要依次加上一个括号使它们得到匹配。综上所述,所求=原序列括号数量-最大匹配括号数量。(因此此题的代码与上题几乎一致)。

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 105;
    const ll mod = 1e9+7;
    const ll INF = 1e18;
    const double eps = 1e-9;
    
    char s[maxn];
    int dp[maxn][maxn];
    
    int main()
    {
        rush()
        {
            scanf("%s",s+1);
            int n=strlen(s+1);
            mst(dp,0);
            for(int len=2;len<=n;len++)
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                if(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')
                {
                    dp[i][j]=dp[i+1][j-1]+2;
                }
                for(int k=i;k<j;k++)
                {
                    dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]);
                }
            }
            printf("%d\n",n-dp[1][n]);
        }
        return 0;
    }
    

     


    3. 整数划分问题

     

     

    题目链接

     

    整数划分(四)

    时间限制:1000 ms  |  内存限制:65535 KB

    难度:3

    描述

    暑假来了,hrdv又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近遇到了一个难题,让他百思不得其解,他非常郁闷。。亲爱的你能帮帮他吗?

    问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积

    输入

    第一行是一个整数T,表示有T组测试数据
    接下来T行,每行有两个正整数 n,m ( 1<= n < 10^19, 0 < m <= n的位数);

    输出

    输出每组测试样例结果为一个整数占一行

    样例输入

    2

    111 2

    1111 2

    样例输出

    11

    121

     

     


    【题意】

     

    给出一个数n,要求在n的数位间插入(m-1)个乘号,将n分成了m段,求这m段的最大乘积。

     

    【思路】

     

    用dp[i][j]表示从第一位到第i位共插入j个乘号后乘积的最大值。根据区间DP的思想我们可以从插入较少乘号的结果算出插入较多乘号的结果。

     

    方法是当我们要放第j的乘号时枚举放的位置。

     

    状态转移方程为

     

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

     

    其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。

     

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 25;
    const ll mod = 1e9+7;
    const ll INF = 1e18;
    const double eps = 1e-9;
    
    int m;
    char s[maxn];
    ll dp[maxn][maxn];
    ll num[maxn][maxn];
    
    int main()
    {
        rush()
        {
            scanf("%s%d",s+1,&m);
            mst(dp,0);
            int len=strlen(s+1);
            for(int i=1;i<=len;i++)
            {
                num[i][i]=s[i]-'0';
                for(int j=i+1;j<=len;j++)
                {
                    num[i][j]=num[i][j-1]*10+s[j]-'0';
                }
            }
            for(int i=1;i<=len;i++)
            {
                dp[i][0]=num[1][i];
            }
            for(int j=1;j<m;j++)
            for(int i=j+1;i<=len;i++)
            for(int k=j;k<i;k++)
            {
                dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i]);
            }
            printf("%lld\n",dp[len][m-1]);
        }
        return 0;
    }
    

     


    4. 凸多边形三角划分问题

     

    Problem Description

    给定一个具有N(N<=50)个顶点(从1到N编号)的凸多边形,每个顶点的权值已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权值的乘积之和最小。

     

    Input

    第一行为顶点数N,第二行为N个顶点(从1到N)的权值。

     

    Output

    乘积之和的最小值。题目保证结果在int范围内。

     

    Sample Input

    5

    1 6 4 2 1

    5

    121 122 123 245 231

     

     

     

    Sample Output

     

    34

    12214884

     

     

     

    【题意】

     

    RT

     

    【思路】

     

    用dp[i,j]表示从顶点i到顶点j的凸多边形三角剖分后所得到的最小乘积。

     

    那么可以写出状态转移方程,并通过枚举分割点来转移。

     

    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);

     

     

     

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 55;
    const ll mod = 1e9+7;
    const int INF = 0x3f3f3f3f;
    const double eps = 1e-9;
    
    int a[maxn];
    int dp[maxn][maxn];
    
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
            }
            mst(dp,0);
            for(int len=3;len<=n;len++)
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                dp[i][j]=INF;
                for(int k=i+1;k<j;k++)
                {
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
                }
            }
            printf("%d\n",dp[1][n]);
        }
        return 0;
    }
    

     

     

     


    四、拓展例题

     

    1. HDOJ 2513 Cake slicing

     

    题目链接

    Cake slicing

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit:32768/32768 K (Java/Others)
    Total Submission(s): 283    Accepted Submission(s): 139

     

    Problem Description

    A rectangular cake with a grid of m*n unit squares on its top needs to be slicedinto pieces. Several cherries are scattered on the top of the cake with at mostone cherry on a unit square. The slicing should follow the rules below:
    1.  each piece is rectangular or square;
    2.  each cutting edge is straight and along a grid line;
    3.  each piece has only one cherry on it;
    4.  each cut must split the cake you currently cut two separate parts

    For example, assume that the cake has a grid of 3*4 unit squares on its top,and there are three cherries on the top, as shown in the figure below.

     

    One allowable slicing is as follows.

     

    For this way of slicing , the total length of the cutting edges is 2+4=6.
    Another way of slicing is

     


    In this case, the total length of the cutting edges is 3+2=5.

    Give the shape of the cake and the scatter of the cherries , you are supposedto find
    out the least total length of the cutting edges.

     

     

    Input

    The input file contains multiple test cases. For each test case:
    The first line contains three integers , n, m and k (1≤n, m≤20), where n*m isthe size of the unit square with a cherry on it . The two integers showrespectively the row number and the column number of the unit square in thegrid .
    All integers in each line should be separated by blanks.

     

     

    Output

    Output an integer indicating the least total length of the cutting edges.

     

     

    Sample Input

    3 4 3

    1 2

    2 3

    3 2

     

     

    Sample Output

    Case 1: 5

     

     

    【题意】

     

    有一个n*m大小的蛋糕,上面有k个樱桃,现在我们需要把这个蛋糕切成k份,使每份蛋糕上有一个樱桃,问最小切割长度和。(切割一刀必须切到底)

     

    【思路】

     

    用dp[i][j][k][l]表示以(i,j)为左上角,(k,l)为右下角的矩形切成每份一个樱桃的最小切割长度。然后就利用区间DP的作用,枚举切割点,从小区间转移到大区间。由于这道题不同区间樱桃个数不同,故用递归的写法会方便些。

     

     

     

     

    #include <cstdio>
    #include <cmath>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 25;
    const ll mod = 1e9+7;
    const int INF = 0x3f3f3f3f;
    const double eps = 1e-9;
    
    int n,m,k;
    int dp[maxn][maxn][maxn][maxn];
    bool flag[maxn][maxn];     //记录某个点是否有樱桃
    
    int fun(int a,int b,int c,int d)
    {
        if(dp[a][b][c][d]!=-1)  //已经计算过
        {
            return dp[a][b][c][d];
        }
        int cnt=0;
        for(int i=a;i<=c;i++)
        for(int j=b;j<=d;j++)
        {
            if(flag[i][j])
                cnt++;
        }
        if(cnt<=1)            //区域内樱桃个数小于2,那么不用切割
        {
            return dp[a][b][c][d]=0;
        }
        int Min=INF;
        for(int i=a;i<c;i++)  //横着切
        {
            Min=min(Min,fun(a,b,i,d)+fun(i+1,b,c,d)+(d-b+1));
        }
        for(int i=b;i<d;i++)  //竖着切
        {
            Min=min(Min,fun(a,b,c,i)+fun(a,i+1,c,d)+(c-a+1));
        }
        return dp[a][b][c][d]=Min;
    }
    
    int main()
    {
        int cas=1;
        int x,y;
        while(~scanf("%d%d%d",&n,&m,&k))
        {
            mst(dp,-1);
            mst(flag,0);
            for(int i=0;i<k;i++)
            {
                scanf("%d%d",&x,&y);
                flag[x][y]=1;
            }
            int ans=fun(1,1,n,m);
            printf("Case %d: %d\n",cas++,ans);
        }
        return 0;
    }
    

     

    2. HDOJ 2476 String painter

    题目链接

    String painter

    Time Limit: 5000/2000 MS (Java/Others)    Memory Limit:32768/32768 K (Java/Others)
    Total Submission(s): 4691    Accepted Submission(s): 2208

     

    Problem Description

    There are two strings A and B with equal length. Both strings are made up of lowercase letters. Now you have a powerful string painter. With the help of thepainter, you can change a segment of characters of a string to any othercharacter you want. That is, after using the painter, the segment is made up ofonly one kind of character. Now your task is to change A to B using stringpainter. What’s the minimum number of operations?

     

     

    Input

    Input contains multiple cases. Each case consists of two lines:
    The first line contains string A.
    The second line contains string B.
    The length of both strings will not be greater than 100.

     

     

    Output

    A single line contains one integer representing the answer.

     

     

    Sample Input

    zzzzzfzzzzz

    abcdefedcba

    abababababab

    cdcdcdcdcdcd

     

     

    Sample Output

    6

    7

     


    【题意】

     

    给出字符串A和B,每一次我们可以使用一种颜色(用字母代替)刷任意一个连续子序列,问将字符串A变成字符串B最少需要刷多少次。

     

    【思路】

     

    分析了很久,发现直接去考虑将A串刷成B串非常困难。于是我们考虑间接转化。

     

    用dp[i][j]表示把将一个空白串[i,j]刷成B字符串对应位置的最小次数。

     

    用ans[i]表示把A串的区间[1,i]刷成B串需要的最小次数。

     

    然后状态转移一下就OK啦。

     

     

    #include <cstdio>
    #include <cmath>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define mst(a,b) memset((a),(b),sizeof(a))
    #define rush() int T;scanf("%d",&T);while(T--)
    
    typedef long long ll;
    const int maxn = 105;
    const ll mod = 1e9+7;
    const int INF = 0x3f3f3f3f;
    const double eps = 1e-9;
    
    char s1[maxn],s2[maxn];
    int dp[maxn][maxn];
    int ans[maxn];
    
    int main()
    {
        while(~scanf("%s%s",s1+1,s2+1))
        {
            int n=strlen(s1+1);
            mst(dp,0);
            for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
            {
                dp[i][j]=j-i+1;
            }
            for(int j=1;j<=n;j++)
            for(int i=j;i>=1;i--)
            {
                dp[i][j]=dp[i+1][j]+1;
                for(int k=i+1;k<=j;k++)
                {
                    if(s2[i]==s2[k])        //这样的话s2[i]可以跟左半区间一起刷到
                    {
                        dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
                    }
                }
            }
            for(int i=1;i<=n;i++)
            {
                ans[i]=dp[1][i];
                if(s1[i]==s2[i])
                {
                    ans[i]=min(ans[i],ans[i-1]);
                }
                else 
                for(int j=1;j<i;j++)
                {
                    ans[i]=min(ans[i],ans[j]+dp[j+1][i]);  //已得出的最优解+剩下的按空白串去刷
                }
            }
            printf("%d\n",ans[n]);
        }
        return 0;
    }
    

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 首先有回的重抽样bootstrap构建模拟的预试验数据集,将结果储存在sampling_desult中,再构造一个和原始data结构相同的初始为空数据集bootstrap_data,然后将重抽样抽到的受试者入这个数据集中,这个过程也可使用...
  • 区间模糊排序

    千次阅读 2009-05-23 17:18:00
    亦即,给定的是n个形如[a(i), b(i)]的闭区间(这里小括后起下标的作用,后同),其中a(i) ,使得存在一个c(j)属于区间[a(i(j)), b(i(j))],满足c(1) a) 为n个区间的模糊排序设计一个算法。你的算法应该具有算法的
  • 使用Python计算bootstrap置信区间

    千次阅读 2018-12-02 17:53:07
    假设总体的分布F未知,但有一个容量为n的来自分布F的数据样本,自这一样本按有回抽样的方法抽取一个容量为n的样本,这种样本称为bootstrap样本。相继地、独立地自原始样本中抽取很多个bootstrap样本,利用这些样本...
  • java数字区间重叠校验

    千次阅读 2013-02-22 19:58:31
    在具体的软件开发过程中可能会涉及到用两个数字表示一定的数字区间范围,或者是一个数字,另一个数字是无穷大或者是无穷小的数字区间范围。其实,总结起来共有8种类型的数字区间,它们是:1、左闭,右边为无穷大的...
  • 单个正态总体,总体方差已知,均值的置信区间,基于中心极限定理、标准正态分布,适用于样本大的情况(n>100) 单个正态总体,总体方差未知,均值的置信区间,基于t分布,适用于样本较小的情况 Bootstrap,...
  • 这个题目有点大,而且我要严格控制字数,不能...事实上,这篇作文是上一篇作文中关于区间查找小节的扩展。1.IP数据包分类根据IP数据包协议头的若干字段,也叫匹配域,将数据包划分到某个类别,这就是IP数据包分类的核心
  • R语言模拟置信区间估计

    万次阅读 2016-11-06 21:55:17
    1.1总体均值的区间估计  方差已知,大样本 代码:  attach(faithful) ##获取火山灰数据 population N mu #sd计算样本方差(注:样本方差除以(n-1)) sigma   layout(matrix(1:1, 1, 1)) ...
  • 作者:Jason Brownlee翻译:和中华校对:丁楠雅本文约4000字,建议阅读15分钟。本文介绍了置信区间的概念以及如何计算置信区间和bootstrap置信区间。机...
  • 每日算法之—数据区间划分

    千次阅读 2018-09-05 12:20:38
    给定一个只包含0,1,2的数组,要求把整个数组进行区间划分,把0划分到左边,1划分到中间,2划分到右边。 如:数组为[0,1,2,1,0,1,2,2,1,0,1,2] 划分结果为:[0,0,0,1,1,1,1,1,2,2,2,2] 要求:空间复杂度为O(1),...
  • Java 生成特定区间的随机数

    千次阅读 2018-01-03 10:23:14
    既然 rand.nextInt(100) 获得的值是区间 [0, 99],那么在这个区间左右各加 1,就得到了区间 [1, 100]。因此,代码写成: rand.nextInt(100) + 1; 即可运行下面的代码,将获得 [1, 100] 的 10 个取值 import ...
  • 上一节我们介绍了Greedy Match的基本原理。本节中,我们将在不同样本的情况下分别进行安慰剂检验,以验证Greedy Match的结果是否真实可信。同时,我们也会提出几种估算该匹配置信区间的方法,使结果更加完善。
  • 线段树详解(单点更新与成段更新\区间更新操作)

    千次阅读 多人点赞 2015-07-06 15:57:13
    本文纯属原创,转载请注明出处,谢谢。 距离第一次接触线段树已经一年多了,再次参加ACM...而作为解决反复对区间的更新和查询问题最好的数据结构,它拥有其他数据结构无法取代的地位。树状数组虽然也能解决很多问题,
  • 题意:有一块h*w 的广告版,有n块1*w[i]的广告,就着广告尽量在顶上,尽量先左边的原则,问在第几行能把广告放下,如果放不下,就打印-1; 思路:我们可以根据每一行建树,每一个子叶表示每一行的容量,而节点...
  • 我讲述了通过爬虫接口得到股票数据并绘制出K线均线图形的方式,在本文里,将在此基础上再引入成交效果图,并结合价理论,给出并验证一些交易策略。 1成交对量化分析的意义 美国的股市分析家葛兰碧(Joe ...
  • 这个输入稍微比调用copy少,但它发生的也比说的要直接:数据插入v1。调用copy也表达了那个意思,但没那么直接。这把重点到了错误的地方。对于发生了什么的关注点不应该是元素被拷贝,而是有新的数据加入v1。...
  • 置信区间与 Z 检验先修   统计学(二)中关于金钱与幸福指数的案例,它的样本只有一个被试。然而,正如我们所说的那样,在实际的例子当中,各领域的研究中大多数都是一个样本中包含着许多个体。所以本篇博文将考虑...
  • 标记已缓存区间 音频播放 Seek操作 清理缓存 流程图 示例程序 播放和暂停 更新定时器 前进和后退 进度条拖动 状态变化通知 退出播放 效果演示 最终总结 代码链接 对于移动开发来说,省流量是必须的。前三篇讲了用...
  • java项目——大数据的处理

    万次阅读 2016-01-31 23:40:36
    s 用n-2个点等分区间[min, max],即将[min, max]等分为n-1个区间(前闭后开区间),将这些区间看作桶,编号为 ,且桶 的上界和桶i+1的下届相同,即每个桶的大小相同。每个桶的大小为: 。实际上,这些桶的边界构成了...
  • 回采样和无回采样

    万次阅读 2017-09-26 10:09:44
    随机采样 可以分为随机欠采样和随机过采样...随机欠采样有两种类型分别为有回和无回两种,无回欠采样在对多数类某样本被采样后不会再被重复采样,有回采样则有可能。 回子采样:bagging(bootstrap agg...
  • 连续傅里叶逆变换中的函数X(ω)是频域连续的,现在假设在X(ω)的某一段连续区间上按照频域抽取N个频率,得到N个采样点,则每个采样点的离散傅里叶逆变换公式就是: x ( n ) = 1 N ∑ k = 0 N − 1 X ( k ) e i 2 ...
  • Oracle数据库是由一个个表空间构成,表空间是由多个数据文件,数据文件又是区间构成,区由Oracle块构成。下图借鉴自:https://www.cnblogs.com/chengxiao/p/5904783.html 1.Oracle表空间数据文件大小的限制 ...
  • @param c 集合,它的元素被用来入列表t * @ throws NullPointerException 如果指定集合为 null */ public ArrayList(Collection extends E> c) { elementData = c.toArray(); // 用Collection初始化...
  • Unity3d轻渲染管线(LWRP)民间文档

    万次阅读 2018-11-21 15:27:51
    近日在学习Unity3d的SRP,由于官方未正式发布,故几乎没有文档支持,考虑...目前只完成了轻管线部分的文档,先发布初版,欢迎指正。后续还有Core库、高清管线和ShaderGraph的文档正在编写中。 Runtime Lightwei...
  • ----股价区间的成交分析---影响股价走势的关键是什么?是成交。如果脱离了成交只看K线图那赔光是早晚的事,是价的先驱,在低位因的放大才会有价的上涨,而在高位也会因的放大从而引起价格的下跌。成交...
  • 本文代码实现基本按照《数据结构》课本目录顺序,外加大量的复杂算法实现,一篇文章足够。能换你一个收藏了吧?
  • 成交分析准则

    千次阅读 2007-06-25 20:53:00
    价格和成交是技术分析的最基本的要素。因此在进行技术分析时一般都特别重视成交分析。在股市里经常听到“在价先”、“成交不会骗人”等说法,还有“天量天价,地地价”等等。可见成交分析在股市中占有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,847
精华内容 23,538
关键字:

区间放量