zoukankan      html  css  js  c++  java
  • 【专题】区间dp

    1.【nyoj737】石子合并

    传送门:点击打开链接

    描述    有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]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]),dp[i][j]代表从i到j合并的最小值,sum[i][j]代表从i到j的价值和。

    区间dp的循环是从小区间到大区间,根据这一规律写出循环结构。

    注意dp[i][i]是0。

    代码:

    #include<bits/stdc++.h>  
    using namespace std;  
    int dp[205][205],a[205],sum[205][205];  
    int main()  
    {  
        int n,i,j,k;  
        while(cin>>n)  
        {  
            memset(sum,0,sizeof(sum));  
            memset(dp,0x3f3f3f3f,sizeof(dp));  
            for(i=1; i<=n; i++)  
            {  
                scanf("%d",&a[i]);  
                dp[i][i]=0;  
                sum[i][i]=a[i];  
            }  
            for(i=1; i<n; i++)  
            {  
                for(j=i+1; j<=n; j++)  
                    sum[i][j]+=sum[i][j-1]+a[j];  
            }  
            for(int len=2; len<=n; len++)  
                for(i=1; i<=n-len+1; i++)  
                {  
                    j=i+len-1;  
                    for(k=i; k+1<=j; k++)  
                        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);  
                }  
            printf("%d
    ",dp[1][n]);  
        }  
        return 0;  
    }  
    View Code

    2.【nyoj37】回文字符串

    传送门:点击打开链接

    给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。

    样例输入

    1
    Ab3bd

    样例输出

    2

    思路1:

    利用反串找LCA

    #include<bits/stdc++.h>  
    using namespace std;  
    char s[1005];  
    int dp[1005][1005];  
    int main()  
    {  
        int t,i,j;  
        cin>>t;  
        while(t--)  
        {  
            scanf("%s",s);  
            int n=strlen(s);  
            memset(dp,0x3f3f3f3f,sizeof(dp));  
            dp[0][0]=0;  
            for(i=1; i<n; i++)  
                dp[i][i]=dp[i][i-1]=0;  
            for(int len=2; len<=n; len++)  
                for(i=0; i<=n-len; i++)  
                {  
                    j=i+len-1;  
                    if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];  
                    else dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);  
                }  
            printf("%d
    ",dp[0][n-1]);  
        }  
    } 
    View Code

    思路2:

    s[i]=s[j],dp[i][j]=dp[i+1][j-1];若!=,dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1)

    解释一下,假如回文串是「1123」,dp[1][4] = min(dp[1][3],dp[2][4]) + 1,先把「112」看成一个整体,dp[1][3] = 1,就是说再补一个就可以回文,最后加上一个「3」,也就是 +1 操作;「123」同理。

    注意dp[i][i]=0,当len=2时,若s[i]=s[j]则dp[i][j]=0,所以初始化dp[i][i-1]也要全部赋为0

    #include<bits/stdc++.h>  
    using namespace std;  
    char s[1005];  
    int dp[1005][1005];  
    int main()  
    {  
        int t,i,j;  
        cin>>t;  
        while(t--)  
        {  
            scanf("%s",s);  
            int n=strlen(s);  
            memset(dp,0x3f3f3f3f,sizeof(dp));  
            dp[0][0]=0;  
            for(i=1; i<n; i++)  
                dp[i][i]=dp[i][i-1]=0;  
            for(int len=2; len<=n; len++)  
                for(i=0; i<=n-len; i++)  
                {  
                    j=i+len-1;  
                    if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];  
                    else dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);  
                }  
            printf("%d
    ",dp[0][n-1]);  
        }  
    } 
    View Code

     

    3.【nyoj15】括号匹配(二)

    传送门:点击打开链接

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

    输入
    第一行输入一个正整数N,表示测试数据组数(N<=10)
    每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100
    输出
    对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行
    样例输入
    4
    []
    ([])[]
    ((]
    ([)]
    样例输出
    0
    0
    3
    2
    思路:
    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]),特殊情况当s[i]与s[j]匹配时,dp[i][j]=min(dp[i][j],dp[i+1][j-1])
    注意当截取长度为2且s[i]与s[j]匹配时,不需要添加符号,所以初始化dp[i][i-1]=0,dp[i][i]=1。
    #include<bits/stdc++.h>  
    using namespace std;  
    char s[105];  
    int dp[105][105];  
    int main()  
    {  
        int t,i,j,k;  
        cin>>t;  
        while(t--)  
        {  
            scanf("%s",s);  
            int n=strlen(s);  
            memset(dp,0x3f3f3f3f,sizeof(dp));  
            dp[0][0]=1;  
            for(i=1; i<n; i++)  
            {  
                dp[i][i]=1;  
                dp[i][i-1]=0;  
            }  
            for(int len=2; len<=n; len++)  
                for(i=0; i<=n-len; i++)  
                {  
                    j=i+len-1;  
                    if(s[i]=='['&&s[j]==']'||s[i]=='('&&s[j]==')')  
                        dp[i][j]=dp[i+1][j-1];  
                    for(k=i; k+1<=j; k++)//这里不是else  
                        dp[i][j]=min(dp[i][k]+dp[k+1][j],dp[i][j]);  
                }  
            printf("%d
    ",dp[0][n-1]);  
        }  
    }
    View Code

     

    4.【nyoj746】整数划分(四)

    传送门:点击打开链接

    给出两个整数 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

    思路:

    由于最大的划分所有的乘号都是固定的,可以从前到后以此找出所有乘号的位置,找出最优子结构(从结论入手向前拆分问题)。

    dp[i][j]代表0到i个数字中添加j个乘号,要求dp[n][m]即求dp[k][m-1]*a[k+1][i],应用到所有dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]),a[i][j]是从i到j的组合数。
    #include<bits/stdc++.h>  
    using namespace std;  
    typedef long long LL;  
    LL dp[25][25],a[25][25];  
    char s[25];  
    int main()  
    {  
        LL n,m,t,i,j,k;  
        cin>>t;  
        while(t--)  
        {  
            scanf("%s%lld",s,&m);  
            n=strlen(s);  
            memset(dp,0,sizeof(dp));  
            for(i=0; i<n; i++)  
                a[i][i]=s[i]-'0';  
            for(i=0; i<n-1; i++)  
                for(j=i+1; j<n; j++)  
                    a[i][j]=a[i][j-1]*10+(s[j]-'0');  
            for(i=0; i<n; i++)  
                dp[i][0]=a[0][i];  
            for(j=1; j<=m-1; j++)  
                for(i=1; i<n; i++)  
                    for(k=0; k+1<=i; k++)  
                        dp[i][j]=max(dp[i][j],dp[k][j-1]*a[k+1][i]);  
            printf("%lld
    ",dp[n-1][m-1]);  
        }  
        return 0;  
    }  
    View Code
  • 相关阅读:
    [歌词]世界末日
    AJAX是什么?
    [转]Moving Your Access 2002 Database to SQL Server
    .net from身份验证的配置介绍
    ajax
    cmd常用命令
    查看端口解除端口占用
    好久没进来了,今天发一个原创的DatePicker
    真倒霉,前不久分區表錯誤把我數據全部搞沒了
    DataGrid利用DataView过滤,排序
  • 原文地址:https://www.cnblogs.com/lesroad/p/8440010.html
Copyright © 2011-2022 走看看