区间dp1

//一般区间DP实现代码
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) //区间长度为1的初始化
dp[i][i] = 0;
for (int len = 2; len <= n; len++) //枚举区间长度
{
for (int i = 1, j = len; j <= n; i++, j++) //区间[i,j]
{
//DP方程实现
}
}
dp[i][j] i 是左区间 j是右区间

memset(dp,0,sizeof(dp))//初始dp数组
for(int len=2;len<=n;len++){//枚举区间长度
    for(int i=1;i<n;++i){//枚举区间的起点
        int j=i+len-1;//根据起点和长度得出终点
        if(j>n) break;//符合条件的终点
        for(int k=i;k<=j;++k)//枚举最优分割点
            dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);//状态转移方程
    }
}

例题1:--------------------------------------------------------------------------------------
【题目描述】
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

计算出将N堆石子合并成一堆的最小得分。

【输入】
第一行为一个正整数N (2≤N≤100);

以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1≤i≤N)。

【输出】
一个正整数,即最小得分。

【输入样例】
7
13
7
8
16
21
4
18
【输出样例】
239
思路:
不同的合并方式最后的结果也不同所以利用区间来考虑合并;dp[i][j]表示 合并i----j这个区间内的所有石子所花费的最小值
那我们可以再去把 i–j细分啊 再去分成 i—k 和k+1----j 然后去跟dp[i][j]比较 赋给它最小值
所以 dp式子为
dp[i][j]=dp[i][k]+dp[k+1][j]+a[j]-a[i-1];
这里需要注意前缀和,每个值都为 a[i]+=a[i-1];
比方说你想求 2-3的和 s=a[3]-a[2-1];

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
long long a[105], dp[105][105];
int main()
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i]; a[i] += a[i - 1];
	}
	//dp[i][j] 表示合并i-j堆花费的最少费用
	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] + a[j] - a[i - 1])
					dp[i][j] = dp[i][k] + dp[k + 1][j] + a[j] - a[i - 1];
			}
		}
	}
	cout << dp[1][n] << endl;
}

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

思路①
dp[i][j]为i–j 之间的最大匹配度
如果 i和j上的匹配 那么dp[i][j]=dp[i+1][j-1]+2;
否则
就再去分割区间 类似于上一个例题。

for (int len = 2; len <= n; len++)
    {
        for(int i = 1, j = len; j <= n; i++, j++)
        {
            if((a[i]=='('&&a[j]==')') || (a[i]=='['&&a[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];
        }
    }
    printf("%d\n",dp[1][n]);
}

思路②
涉及到了 第二种模型在这里插入图片描述
在这里插入图片描述
第二种模型就是根据匹配信息把区间划分成[i+1,k-1]和[k+1,j]

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]);

例题3
题意:有N个宴会,对于每一个宴会,女猪脚都要穿一种礼服,礼服可以套着穿,但是脱了的不能再用,参加宴会必须按顺序来,从第一个到第N个,问参加这些宴会最少需要几件礼服,拿第一个案例来说
4
1 2 1 2,有4个宴会,第一个需要礼服种类为1,第二个需要礼服种类为2,以此往下推:

参加第一个宴会时穿礼服1,参加第二个时,礼服1不要脱下,直接把礼服2套在外面,参加第三个的时候把礼服2脱下即可,参加第四个则需要一件新的礼服了
思路:
dp[i][j]代表从区间i到区间j最少的穿衣数量,那么在dp[i][j]这个状态的穿衣数,就要等于dp[i+1][j]+1;也就是说,首先在不考虑它后面是否有一天要穿相同的衣服的情况下,它肯定会比区间i+1到j的衣服多出一件;

然后,再考虑在这个区间范围,是否有一天要穿相同的衣服,i<k<=j,如果有第k天衣服和第i天的衣服是一样的,那么就要比较如果第i天不穿1件衣服与第i天穿上1件衣服;

首先,第i天穿上一件衣服的结果已经得出,那么我们只需比较不穿衣服,那么就是dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]);

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <stdio.h>
 
using namespace std;
int n;
int a[105];
int dp[105][105];
int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        memset(dp,0,sizeof(dp));
 
        for(int i=n;i>=1;i--)
        {
            for(int j=i;j<=n;j++) 从后往前枚举,并且后面的最优解已经枚
            举过了,因为实际是从前往后进行,所以从后枚举不会造成影响
            {
                dp[i][j]=dp[i+1][j]+1;  
                先直接给他加上,因为说不
                定这件衣服后面好多件都要用到
                for(int k=i+1;k<=j;k++)
                {
                    if(a[i]==a[k])
                    dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);  //这里不要是i+1,不是i,因为在讨论是不是脱i呢
                }
            }
        }
        printf("Case %d: %d\n",++cas,dp[1][n]);
 
    }
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值