说起动态规划,哈哈,在最初听到这个名字的时候就非常感兴趣,但是直到看题之后发现有些题目好难理解,或者说是很难整理出关系, 每次决策依赖于当前状态,又随即引起状态的转移,多阶段最优化,这样结果一定不差!分析了一些题目,有些看了题解还是搞不懂,还需要再重复看,以下是几个题的分析:
1、午餐 P2577 [ZJOI2004]午餐 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:n个人每个人有一个打饭时间和吃饭时间,将他们分成两个队伍。每个人打到饭之后就马上去吃饭。问怎么安排可以让总体的吃饭时间最短。
分析:某个队伍的总吃饭时间实际上是打饭结束+吃饭时间最晚的那个时间。首先,我们假设只有一个窗口的时候怎么排序是最优的,这里有一个重要的发现就是:当把学生的吃饭时间从大到小排序后再安排学生以这个顺序打饭是最优的。有了这一个重要的发现后,我们可以排序,在排序后那么怎么把学生在两个窗口之间分配呢?我们可以给学生排在一号窗口或者排在二号窗口。
其中dp[i][j]表示当前已经有i个人,在一号窗口排队时间(注意不包含吃饭时间)累计为j的时候最短的最后一个吃完饭的学生的时间。ti[i].first代表第i个学生的吃饭时间,ti[i].second代表第i个学生的打饭时间。sum[i]表示到第i个学生的打饭总时间。
dp[0][0]=ti[0].first+ti[0].second;
dp[0][ti[0].second]=ti[0].first+ti[0].second;
for(int i=1;i<n;i++)
for(int j=0;j<=sum[i];j++){
if(j>=ti[i].second )
dp[i][j]=min(dp[i][j],max(dp[i-1][j-ti[i].second],j+ti[i].first));
dp[i][j]=min(dp[i][j],max(dp[i-1][j],sum[i]-j+ti[i].first));
2、最大双向子段和 OpenJudge - 2479:Maximum sum
题意:给n个数,求出两个无交叉子段和最大的最大值。
分析:我们可以把它分成两个单向子段和的相加,DP[i][0]与DP[i][1],DP[i][0]表示的是从0~i中连续子串的最大和,DP[i][1]表示i~n-1中连续子串的最大和,则题目就变成求max{DP[i][0]+DP[i+1][1]},即左串+右串
部分代码:
int main(){
int t;
cin >> t;
while(t --){
int n;cin >> n;
for(int i = 0;i < n;i ++){
cin >> a[i];
if( i )
dp[i][0] = max(dp[i - 1][0] + a[i],a[i]);
else
dp[i][0] = a[i];
}
for(int i = n - 1;i >= 1;i --){
if( i != n - 1)
dp[i][1] = max(dp[i + 1][1] + a[i],a[i]);
else
dp[i][1] = a[i];
}
ll ans = -1e18;
for(int j = 0;j < n - 1;j ++)
for(int i = j + 1;i < n ;i ++){
ans = max(ans,dp[j][0] + dp[i][1]);
}
cout << ans << endl;
}
3、丝绸之路 P3399 丝绸之路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:M天内到达城市N,求最小的疲劳度
分析:从原点到终点的n+1个城市穿越任务要在m天内完成,必须按给出的城市顺序来完成整个路程,两个城市之间有间距,在这m天中每天会有一个恶劣值,城市之间的距离乘以这一天的恶劣值是这个疲劳度,求穿越所有城市后疲劳度的最小值。题目很容易理解,但是题目读懂之后却不清楚如何来使用DP,先看了一些大佬的题解。多加一个维度来判断那一天走这个城市,二维的列坐标是代表应该走过前几个城市,那个二维的行坐标就是表示在这个天数,用i表示列,j表示行,dp[i][j]表示在前j天内走过i个城市所需要的最小疲劳度:
dp[i][j]=min(dp[i][j-1],dp[i-1][j-1]+C[j]*D[i]);
这个题还需要再思考思考。
4、传纸条 275. 传纸条 - AcWing题库
题意:给定一个 N ∗ M的矩阵A,每个格子中有一个整数。现在需要找到两条从左上角 ( 1 , 1 ) 到右下角 ( N , M ) 的路径,路径上的每一步只能向右或向下走。路径经过的格子中的数会被取走。两条路径不能经过同一个格子。求取得的数之和最大是多少。
分析:这个题看过大佬的题解,图文结合最终大概理解了,多维,又是一个写不出来的代码。这个题还需要处理的一个问题是路径没有重复点,但是如果有重复的点,从起点到这个点的任意走法步数是一样的,所以可以用f[k][i][j]表示总共走了k步,第一个人走到了(i,k-i),第二个人走到了(j,k-j),此时能获得的最大值。两个人走到了同一个点当且仅当i==j&&k!=2&&k!=m+n的时候。然后f[k][i][j]的状态由四种转化而来:第一种是两个人都向右,第二种是都向下,还有两种是一个向右一个向下。
5、回文子串 P1435 [IOI2000] 回文字串 / [蓝桥杯 2016 省] 密码脱落 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:求出将给定字符串变成回文词所需要插入的最少字符数
分析:这个题容易理解,但我也没有瞬间想出如何用DP做。可以求出最长回文子序列来,
然后用原字符串长度减去最长回文子序列的长度即可。对于回文的,已经算在最长回文子序列的长度中,其他的非回文的,就可以填上一个字母,所以,需要给非回文填上的字母个数即原先的长度减去最长回文子序列的长度。关键:求最长回文子序列的长度
部分代码:
for (int step = 1; step < n; step++)
{
for (int i = 1; i + step <= n; i++)
{
int j = i + step;
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
if (str[i] == str[j])
{ dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);}
}
}
总体来说,这一块对我来说是个大问题,需要花更多的时间来学习做题,但我对此有信心!!!