其他分享
首页 > 其他分享> > 第七周学习总结。

第七周学习总结。

作者:互联网

每周学习总结:第七周;

本周学习:动态规划:区间dp的(三种模型)。

第一种模型:(合并石子)类;

含义: 从任意一个位置分成两个区间,如:dp[i][k]、dp[k+1][j];一个是区间由i到k,另外一个为k+1到j;就是从i到j区间中找到一个k使之分为两个区间。
例题:
在一圆形操场四周摆放N堆石子 , 现要将石子有次序地合并成一堆.规定每次只能选相临的两堆合并成一堆,并将新的一堆的石子数,记为该次合并的代价。

编一程序,由文件读入堆数N及每堆石子数,

选择一种合并石子的方案,使得做N-1次合并,代价的总和最少。
题解:
dp[i][j](为第i个到第j个的最小代价。)
DP方程: dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);//s[i]表示前i堆石头数量总和

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dp[101][101],sum[101],n,x;//初始化
int main()
{
	memset(dp,127/3,sizeof(dp));//先把f里的所有数变成一个很大的数
	scanf("%d",&n);//读入
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);//读入
		sum[i]=x+sum[i-1];//计算前缀和
		dp[i][i]=0;//标记初始的值
	}
	for (int i=n-1;i>=1;i--)
	for (int j=i+1;j<=n;j++)
	for (int k=i;k<=j-1;k++)
	dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);//状态转移方程
	printf("%d",dp[1][n]);//输出
	return 0;
}

第二种模型:

含义: 将根据匹配信息将区间划分为[i+1,k-1]和[k+1][j]这两个区间,相当于模型一中将区间中的i项和找到的k项提了出来,所划的区间。
例题:(小括号中括号问题)

题目: 给一个括号组成的字符串,问最多能匹配多少个括号
像([)]这 样的字符串匹配度为2,但是像()[]、[()]的字符串匹配度为4,也就是说括号具有分隔作用。
长度为1的串匹配度 0
长度为2的串匹配度 0 或 2

题解:
规定dp[i][j]为合并第i到第j个字符的最大匹配度

长度为n时,我们可以先检测a[i]和a[j]是否匹配,
匹配dp[i][j] = dp[i+1][j-1] + 2
我们可以把[i,j]区间的字符当成由[i+1,j]在前面加个字符或[i,j-1]在后面加一个字符得来的.
这里我们只考虑[i,j]由[i+1,j]在前面加一个字符的情况
如果a[i+1]到a[j]没有和a[i]匹配的,
那么dp[i][j] = dp[i+1][j]
如果a[k]和a[i]匹配(i < k <= j),那么
dp[i][j] = max(dp[i][j], dp[i+1][k-1] + dp[k+1][j] + 2);
代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int   dp[150][150];
char a[150];
int main()
{
    memset(dp,0x3f,sizeof(dp));
while (gets(a+1))
{
    if(a[1] == 'e') break;
    memset(dp, 0, sizeof(dp));
    int n = strlen(a+1);
    for (int len = 2; len <= n; len++)
    {
        for(int i = 1, j = len; j <= n; i++, j++)
        {
            dp[i][j] = dp[i+1][j];
            for (int k = i; k <= j; k++)
                if((a[i]=='('&&a[k]==')') || (a[i]=='['&&a[k]==']'))
                    dp[i][j] = max(dp[i][j], dp[i+1][k-1] + dp[k+1][j] + 2);
        }
    }
    printf("%d\n",dp[1][n]);
}
}


第三种模型:

含义: 不需要枚举区间k(i<=k<=j),这种类型只和左右边界相关;也就是不用再区间i到j中寻找一个k将它分为两个区间,而是直接再区间i到j的首尾进行操作。
例题:
题意:有N个宴会,对于每一个宴会,女猪脚都要穿一种礼服,礼服可以套着穿,但是脱了的不能再用,参加宴会必须按顺序来,从第一个到第N个,问参加这些宴会最少需要几件礼服,拿第一个案例来说
4
1 2 1 2,有4个宴会,第一个需要礼服种类为1,第二个需要礼服种类为2,以此往下推:
参加第一个宴会时穿礼服1,参加第二个时,礼服1不要脱下,直接把礼服2套在外面,参加第三个的时候把礼服2脱下即可,参加第四个则需要一件新的礼服了
题解:
1.在一个区间 [l, j] 内,如果l是第 k (1<=k<=j-l+1)个出场的,那么 [l+1, l+k-1] 必定是先与l出场的,剩下的 [l+k, j] 是在l之后出场的。

2.如果确定了l在区间 [l, j] 是第k个出场的,可以把区间分为 [l+1, l+k-1] 和 [l+k, j],两个区间互不影响,也可以分别对这两个区间单独求值。

其在当前区间内是第k个出场的,先得到 D[l]*(k-1)。*对于区间[l+1, l+k-1]的人,是先于l出场的,对于总体,
他们是没有延迟的,直接加上dp[l+1, l+k-1]。对于区间[l+k, j]的人,他们是在l之后出场的,l在这个区间内
又是第k个出场的,对于 [l+k, j]这个总体,他们是整体延迟了k个人的,加上 k(sum[j]-sum[l+k-1]),加上了
延迟所带来的值就可以把 区间[l+k, j]的出场是没有延迟的,情况跟区间[l+1, l+k-1]一样,再加上dp[l+k, j]。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int D[1010];
int sum[1010];
int dp[1010][1010];
int main()
{
    int t;
    scanf("%d",&t);
    int Case=1;
    while(t--)
    {
        int n;
        sum[0]=0;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&D[i]);
            sum[i]=sum[i-1]+D[i];
        }

        for(int len=1;len<n;len++)
        {
            for(int i=1;i<=n;i++)
            {
                int j=i+len;
                if(j<=i)
                dp[i][j]=0;
                else
                dp[i][j]=INF;
                for(int k=1;k<=j-i+1;k++)

                    dp[i][j]=min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+(k-1)*D[i]+k*(sum[j]-sum[i+k-1]));
            }
        }
        printf("Case #%d: %d\n",Case++,dp[1][n]);
    }
    return 0;
}

总总结:

1、区间dp 较 线性dp 难理解,上课老师讲的时候,大多处于懵懂状态,课后反反复复看了几遍才略有所见。
2、目前还不能一见到题就知道哪一种类型,还得多练!!
下周目标:
1、每天1~2 题 ,周末 2~~3题。

标签:总结,礼服,第七,int,sum,学习,区间,include,dp
来源: https://blog.csdn.net/qq_51153436/article/details/116106861