区间dp的典例

区间dp, 属于dp的一种,顾名思义,便是对区间处理的dp,其中石子归并,括号匹配,整数划分最为典型。

(1)石子归并

dp三要素:阶段,状态,决策。

首先我们从第i堆石子到第j堆石子合并所花费的最小费用设为dp[i][j], 然后去想状态转移方程,dp[i][j]必然有两堆石子合并而来, 那么我们很快就可以退出状态转移方程为dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + s);(s为两堆石子的总和)

下面附上代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N = 200 + 5;
 6 int a[N], dp[N][N], n, sum[N];
 7
 8 void work(){
 9     for(int l = 1; l <= n; l ++){
10         for(int i = 1; i + l <= n; i ++){
11             int j = i + l;
12             for(int k = i; k <= j; k ++){
13                 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
14             }
15         }
16     }
17     printf("%d\n", dp[1][n]);
18 }
19
20 int main(){
21     while(scanf("%d", &n) == 1){
22         memset(dp, 0x3f,sizeof(dp));
23         for(int i = 1; i <= n; i ++){
24             scanf("%d", a + i);
25             sum[i] = sum[i-1] + a[i];
26             dp[i][i] = 0;
27         }
28         work();
29     }
30     return 0;
31 }

当然还有变形题

思路差不多只不过把两个数的和改成积(ps:在处理前缀和的时候千万别取余,否则可能出现负数)

附上代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N = 200 + 5;
 6 int a[N], dp[N][N], n, ans, sum[N];
 7
 8 void  work(){
 9     for(int l  = 2; l <= n; l ++){
10         for(int i = 1; i <= n - l + 1; i ++){
11             int j = i + l - 1;
12             for(int k = i; k < j; k ++){
13                 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + ((sum[j] - sum[k])%100)*((sum[k] - sum[i-1])%100));
14             }
15         }
16     }
17     printf("%d\n", dp[1][n]);
18 }
19
20 int main(){
21     while(scanf("%d", &n) == 1){
22         for(int i = 1; i <= n; i ++)
23             for(int j = 1; j <= n; j ++)
24                 dp[i][j] = (1 << 30);
25         for(int i = 1; i <= n; i ++){
26             scanf("%d", a + i);
27             sum[i] = sum[i-1] + a[i];
28             dp[i][i] = 0;
29         }
30         work();
31     }
32     return 0;
33 }

(2)括号匹配

这题解释括号匹配的例题,只要找到这个字符串中括号最大匹配量t,就可以得出答案,设长度为l,则ans = l - t;

我们设dp[i][j] 为第i位到第j位最大的括号匹配量, 则他的转移方程为

dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);

当然如果第i位刚好与第j位刚好匹配

则dp[i][j] = dp[i+1][j-1] + 2;

下面附上代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6
 7 string s;
 8
 9 int n, dp[105][105];
10
11 int main(){
12     int T;
13     scanf("%d", &T);
14     while(T--){
15         cin >> s;
16         memset(dp, 0, sizeof(dp));
17         for(int l = 0; l < s.size(); l ++){
18             for(int i = 0; i + l < s.size(); i ++){
19                 int j = i + l;
20                 if(s[i] == ‘(‘ && s[j] == ‘)‘)
21                    dp[i][j] = dp[i+1][j-1] + 2;
22                 if(s[i] == ‘[‘ && s[j] == ‘]‘)
23                     dp[i][j] = dp[i+1][j-1] + 2;
24                 for(int k = i; k <= j; k ++){
25                     dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j]);
26                 }
27             }
28         }
29         printf("%d\n", s.size() - dp[0][s.size()-1]);
30     }
31     return 0;
32 }

(3)整数划分

当初一看到这一题的时候感觉像是搜索题,仔细一想才明白是一道区间dp题,既然是dp,当然要先找到状态了,设dp[i][j]为前i位中存在j个乘号

我们以a[i][j]表示第i位到第j位的值,则可以推出状态转移方程为dp[i][j] = max(dp[i][j], dp[i][k] * a[k+1][j]);

附上代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int N = 100 + 5;
 7
 8 ll a[N][N], dp[N][N];
 9 int n, T, c[N];
10 char s[N];
11
12 void work(){
13     for(int j = 0; j < n; j ++){
14         for(int i = 1; i <= strlen(s); i ++){
15             for(int k = 1; k <= i; k ++){
16                 if(j==0)
17                     dp[i][0] = a[1][i];
18                 else
19                     dp[i][j] = max(dp[i][j], dp[k][j-1] * a[k+1][i]);
20                 /*for(int p = 1; p <= strlen(s); p ++){
21                     for(int q = 0; q < n; q ++)
22                         printf("%d ", dp[p][q]);
23                     puts("");
24                 }*/
25             }
26         }
27     }
28     printf("%lld\n", dp[strlen(s)][n-1]);
29 }
30
31 int main(){
32     scanf("%d", &T);
33     while(T--){
34         scanf("%s%d" , s, &n);
35         int flag = 0;
36         if(n > strlen(s)){
37             printf("0\n");
38             continue;
39         }
40         memset(a, 0, sizeof(a));
41         memset(dp, 0, sizeof(dp));
42         for(int i = 0; i < strlen(s); i ++)
43             c[i+1] = s[i] - ‘0‘;
44         for(int i = 1; i <= strlen(s); i ++){
45             for(int j = i; j <= strlen(s); j ++){
46                     a[i][j] = a[i][j-1] * 10 + c[i];
47                 }
48             }
49         }
50         /*for(int i = 1; i <= strlen(s); i ++){
51             for(int j = i; j <= strlen(s); j ++)
52                 printf("%I64d ", a[i][j]);
53             puts("");
54         }*/
55         work();
56     }
57     return 0;
58 }

时间: 2024-10-09 08:08:27

区间dp的典例的相关文章

P1220 关路灯 区间DP

题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯. 为了给村里节省电费,老张记录下了每盏路灯的位置和功率,他每次关灯时也都是尽快地去关,但是老张不知道怎样去关灯才能够最节省电.他每天都是在天亮时首先关掉自己所处位置的路灯,然后可以向左也可以向右去关灯.开始他以为先算一下左边路灯的总功率再算一下右边路灯的总功率,然后选择先关掉功率大的一边,再回过头来关掉另一边

Luogu P2734 游戏 A Game 区间DP

P2734 游戏 A Game 题目背景 有如下一个双人游戏:N(2 <= N <= 100)个正整数的序列放在一个游戏平台上,游戏由玩家1开始,两人轮流从序列的任意一端取一个数,取数后该数字被去掉并累加到本玩家的得分中,当数取尽时,游戏结束.以最终得分多者为胜. 题目描述 编一个执行最优策略的程序,最优策略就是使玩家在与最好的对手对弈时,能得到的在当前情况下最大的可能的总分的策略.你的程序要始终为第二位玩家执行最优策略. 输入输出格式 输入格式: 第一行: 正整数N, 表示序列中正整数的个数

能量项链 区间dp

能量项链 时间限制: 1 Sec  内存限制: 64 MB提交: 38  解决: 15[提交][状态][讨论版] 题目描述 每 个天顶星人都随身佩戴着一串能量项链,在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠 子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是天顶星人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠 子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗

区间DP经典模型

http://blog.csdn.net/y990041769/article/details/24238547 先附上一个链接 后面有引用的代码 概述 区间 DP:是指在一段区间上进行的一系列动态规划. 对于区间 DP 这一类问题,我们需要计算区间 [1,n] 的答案,通常用一个二维数组 dp 表示,其中 dp[x][y] 表示区间 [x,y]. 有些题目,dp[l][r] 由 dp[l][r-1] 与 dp[l+1][r] 推得; 也有些题目,我们需要枚举区间 [l,r]内的中间点,由两个子

codevs1154能量项链(环形dp,区间dp)

1154 能量项链 2006年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸

NOIP2006 能量项链(区间DP)

[问题描述] 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为(Mars单位),新产生的珠子的头标记为m

BZOJ 1260: [CQOI2007]涂色paint( 区间dp )

区间dp.. dp( l , r ) 表示让 [ l , r ] 这个区间都变成目标颜色的最少涂色次数. 考虑转移 : l == r 则 dp( l , r ) = 1 ( 显然 ) s[ l ] == s[ l + 1 ] 则 dp( l , r ) = dp( l + 1 , r )     s[ r ] == s[ r - 1 ] 则 dp( l , r ) = dp( l , r - 1 )  因为只要在涂色时多涂一格就行了, 显然相等 , 所以转移一下之后更好做 s[ l ] == s

5.1个人赛解题报告(区间dp,按位与或,图论等水题)

这次5.1打了一场个人赛,已经连赛了三周了,有点疲惫感觉,可能自己太水了,每次都有点小紧张. 这次只解出来三道题,然而有一道按位与按位或的水题不知道思路实在是做题太少,还有就是第一题区间DP,也消耗了不少的时间,但是没有成功的写出来,还是不够熟练啊. 下面写报告 A. System Administrator time limit per test 2 seconds memory limit per test 256 megabytes input standard input output

hdu 5693 &amp;&amp; LightOj 1422 区间DP

hdu 5693 题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5693 等差数列当划分细了后只用比较2个或者3个数就可以了,因为大于3的数都可以由2和3组合成. 区间DP,用dp[i][j]表示在i到j之间可以删除的最大数,枚举区间长度,再考虑区间两端是否满足等差数列(这是考虑两个数的),再i到j之间枚举k,分别判断左端点右端点和k是否构成等差数列(还是考虑两个数的),判断左端点,k,右端点是否构成等差数列(这是老驴三个数的) 1 #include