(sdau) Summary of the sixth week.

学习内容:

滚动数组:
滚动数组是DP中的一种编程思想。简单的理解就是让数组滚动起来,每次都使用固定的几个存储空间,来达到压缩,节省存储空间的作用。起到优化空间,主要应用在递推或动态规划中。因为DP题目是一个自底向上的扩展过程,我们常常需要用到的是连续的解,前面的解往往可以舍去。所以用滚动数组优化是很有效的。利用滚动数组的话在N很大的情况下可以达到压缩存储的作用。

节省了空间。

区间动态规划:

区间动态规划,顾名思义,就是动态规划过程中求一个区间的最优解。通过将一个大的区间分为很多个小的区间,求其小区间的解,然后一个一个的组合成一个大的区间而得出最终解。

第一种模型:

石子合并:
石子合并问题是最经典的DP问题。首先它有如下3种题型:
(1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成

分析:当然这种情况是最简单的情况,合并的是任意两堆,直接贪心即可,每次选择最小的两堆合并。

(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式:
在这里插入图片描述

#include<cstring>
#include<cmath>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
//#define Max 0x3f
int sum[1001];
int n,i,j,k,x;
int dp[1001][1001];
int main()
{
    cin>>n;
    for(i=1; i<=n; i++)
        {
            cin>>x;
            sum[i]=sum[i-1]+x;
        }
    memset(dp, 0x3f, sizeof(dp));
    for (int i = 1; i <= n; i++)
        dp[i][i] = 0;
    for (int len = 2; len <= n; len++)
    {
        for (int i = 1, j = len; j <= n; i++, j++)
        {
            for (int k = i; k < 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];
            }
        }
    }
    printf("%d\n", dp[1][n]);
}

(3)问题(2)的是在石子排列是直线情况下的解法,如果把石子改为环形排列。

以后补充……

第二种模型:

括号匹配:
在这里插入图片描述
在这里插入图片描述

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define maxn 105
using namespace std;
int dp[maxn][maxn];
char s[maxn];
int dfs(int l,int r){
    if(l>=r) return 0;
    if(dp[l][r] != -1) return dp[l][r];
    if(s[l]=='('&&s[r]==')'||s[l]=='['&&s[r]==']')
        dp[l][r]=dfs(l+1,r-1)+2;
    for(int i=l;i<r;i++)
        dp[l][r]=max(dp[l][r],dfs(l,i)+dfs(i+1,r));
    return dp[l][r];
}
int main(){
    int n;
    while(~scanf("%s",s)&&s[0]!='e'){
        int n=strlen(s);
        memset(dp,-1,sizeof(dp));
        int ans=dfs(0,n-1);
        printf("%d\n",ans);
    }
    return 0;
}
第三种模型:

不需要枚举区间k,只和左右边界相关。
例题:
在这里插入图片描述
令dp[i][j]表示从位置i到j的子串转化为回文串需要的次数若 s[i]== s[j] 则dp[i][j] = dp[i + 1][j - 1]否则 dp[i][j] = min(dp[i+1][j] + cost[i], dp[i][j - 1] + cost[j])

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAX 2111
using namespace std;
int dp[MAX][MAX];
int cost[300];
char ch[MAX];
int N,M;
int DP()
{
    int i,j,k,r;
    for(i=0;i<M;i++) dp[i][i]=0;
    for(r=2;r<=M;r++)         //子段的长度
        for(i=0;i<=M-r;i++)       //i为首指针,j为尾指针。
        {
            j=i+r-1;
            if(ch[i]==ch[j])
                dp[i][j]=dp[i+1][j-1];
            else
            {
                dp[i][j]=min(dp[i+1][j]+cost[ch[i]],dp[i][j-1]+cost[ch[j]]);
            }
        }
    return dp[0][M-1];  //最优解
}
int main()
{
    int i,j;
    char c;
    int ins,del;
    scanf("%d%d",&N,&M);
    getchar();
    gets(ch);
    for(i=0;i<N;i++)
    {
        scanf("%c%d%d",&c,&ins,&del);
        getchar();
        cost[c]=min(ins,del);
    }
    int ans=DP();
    printf("%d\n",ans);
    return 0;
}

unique去重
unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(详细情况,下面会讲)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。

unique函数的函数原型如下:

1.只有两个参数,且参数类型都是迭代器:
iterator unique(iterator it_1,iterator it_2);
这种类型的unique函数是我们最常用的形式。其中这两个参数表示对容器中[it_1,it_2)范围的元素进行去重(注:区间是前闭后开,即不包含it_2所指的元素),返回值是一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。
unique函数的去重过程实际上就是不停的把后面不重复的元素移到前面来,也可以说是用不重复的元素占领重复元素的位置。

2.unique函数通常和erase函数一起使用,来达到删除重复元素的目的。这里的删除是真正的删除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值