第七周学习总结。
作者:互联网
每周学习总结:第七周;
本周学习:动态规划:区间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