2021-04-26

第八周动态规划区间DP

乘法游戏

题解:
桌子上总会有最左边和最右边的牌,这两张牌在进行过程中,不能被拿走,直到最后剩下这两张牌。
得分的规则是用你拿起的牌乘左侧牌的数再乘右侧牌的数。求得分最小。每次都有一张牌被拿走,与得分有关系的数分别是被拿走的牌,以及它左右两侧的牌,还有最左最右的牌。我们要找出最后被拿走的牌,设这张牌为k,那么就分成了两个区间,最左侧到k和k到最右侧。在小区间中继续分,枚举出所有情况,找出最优解。
d[i][j]表示第i个数到第j个数合并的最小值状态转移方程:
d[i][j]=min(d[i][j],d[i][k]+d[k][j]+a[i]*a[j]*a[k])i<k<j
代码:

#include <stdio.h>
#include<iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int M=105;

int main()
{
    int n,a[M],dp[M][M];
    while(~scanf("%d",&n))
    {
        for(int i = 1; i<=n; i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        for(int l = 2; l<n; l++)
        {
            for(int i = 2; i+l-1<=n; i++)
            {
                int j;
                j = i+l-1;
            
                dp[i][j] = 100000000;
                for(int k = i; k<j; k++)
                    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i-1]*a[k]*a[j]);
            }
        }
        printf("%d\n",dp[2][n]);
    }

    return 0;
}

完整括号问题

这个题其实最主要的还是注意一下刚开始的初始化,这个题目的难点应该就是在这个上面了。
区间dp,dp[i][j]表示[i,j]子串中最长的合法括号子序列是多长。按照状态转移方程,求出最终解。
状态转移方程:

dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
{
    dp[i][j]=max(dp[i][j],dp[i+1][j-1]+2);
}

总代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

const int M=105;
int dp[M][M];
char ch[M];
int i,j,k,t,v;

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    while(gets(ch+1))
    {
        if(ch[1]=='e') break;
        memset(dp,0,sizeof dp);
        int l=strlen(ch+1);
        for(int t=1; t<=l; t++)
        {
            for(int i=1,j=t; j<=l; i++,j++)
            {
                if((ch[i]=='('&&ch[j]==')')||(ch[i]=='['&&ch[j]==']'))
                    dp[i][j]=dp[i+1][j-1]+2;
                for(int k=i; k<j; k++)
                {
                    if(dp[i][j]<dp[i][k]+dp[k+1][j])
                        dp[i][j]=dp[i][k]+dp[k+1][j];
                }
            }
        }
        cout<<dp[1][l]<<endl;
    }
    return 0;
}

愤怒值(小黑屋)

当n=2时:只要考虑两个人谁先谁后,比较两个人的愤怒值,排序即可。
当n>2时,队伍人数扩大后,分成小区间。每个小区间的愤怒值排序要排好。考虑队伍中的第一个人,安排在第几位,设dp[i][j]为第i个人到第j个人能达到的最小总愤怒值。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int M= 105;
const int inf = 0x3f3f3f3f;

int t, n, ans;
int aa[M], bb[M];
int dp[M][M];
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", bb + i);
            bb[i] = bb[i - 1] + aa[i];
        }
        for (int p = 1; p< n; p++)
        {
            for (int l = 1; l + p <= n; l++)
            {

                int r = l + p;
               
             dp[l][r] = inf;
                for (int k = l; k <= r; k++)
                    dp[l][r] = min(dp[l][r], aa[l] * (k - l) + dp[l + 1][k] + dp[k + 1][r] + (bb[r] - bb[k]) * (k - l + 1));
            }
        }
        printf("Case #%d: %d\n", ++ans, dp[1][n]);
    }
    return 0;
}

宴会服装

对于区间(i,j)如果第i件衣服和区间内的其他从i+1到j的衣服都不同,即第i件衣服在这个区间我们只穿一次,那么dp[i][j]=dp[i+1][j]+1;
否则遍历k(i<k<=j),如果a[k]==a[i],那么对于第k个party,可以穿第i件衣服,此时dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]);

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 110;

int a[MAX], n;
int dp[MAX][MAX];

int main() {
    int t, cas = 1;
    scanf("%d\n", &t);
    while (t--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++)
            for (int j = i; j <= n; j++)
                dp[i][j] = j-i+1;

        for (int i = n-1; i >= 1; i--)
            for (int j = i+1; j <= n; j++)
            {
                dp[i][j] = dp[i+1][j] + 1;
                for (int k = i; k <= j; k++)
                    if (a[i] == a[k])
                        dp[i][j] = min(dp[i][j], dp[i+1][k-1]+dp[k][j]);
            }

        printf("Case %d: %d\n", cas++, dp[1][n]);
    }
    return 0;
}

总结

区间DP和线性DP还是很相似的,线性dp是以一个点为对象,建立状态方程,在整个区间中找到最优解; 而区间dp则是以一个区间为对象,找出边界条件从小区间开始不断求解出最终总的区间问题。
区间DP主要是把一个大区间拆分成几个小区间,先求小区间的最优值,然后合并起来求大区间的最优值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值