线性dp

  • A - 超级楼梯 (HDU-2041)
    • 分析
    • 代码
  • B - 一只小蜜蜂... (HDU-2044)
    • 分析
    • 代码
  • C - 母牛的故事 (HDU-2018)
    • 分析
    • 代码
  • D - Common Subsequence (POJ-1458)
    • 分析
    • 代码
  • E - Longest Ordered Subsequence (POJ-2533)
    • 分析
    • 代码
  • F - Super Jumping! Jumping! Jumping! (HDU-1087)
    • 分析
    • 代码
  • G - Jumping Cows (POJ-2181)
    • 分析
    • 代码

网页链接:传送门
密码:HPUACM

A - 超级楼梯 (HDU-2041)

有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
Input
输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1<=M<=40),表示楼梯的级数。
Output
对于每个测试实例,请输出不同走法的数量

Sample Input Sample Output
2
2
3
1
2

分析

这题好像之前做过来着,我还以为我写了题解?但是没有找到(也可能是因为没洗细找⑧)那就在这里解释一波。由于刚开始在第 111 级,每次只能跨上一级或两级,情况就如图所示:

对于第 101010 级楼梯,它可以由第 999 级或第 888 级走到,即 dp[10]=dp[9]+dp[8]dp[10]=dp[9]+dp[8]dp[10]=dp[9]+dp[8] .
对于第 999 级楼梯,它可以由第 888 级或第 777 级走到,即 dp[9]=dp[8]+dp[7]dp[9]=dp[8]+dp[7]dp[9]=dp[8]+dp[7] .
对于第 888 级楼梯,它可以由第 777 级或第 666 级走到,即 dp[8]=dp[7]+dp[6]dp[8]=dp[7]+dp[6]dp[8]=dp[7]+dp[6] .
……
以此类推,直到最后只剩下 ≤3≤3≤3 的级数:
对于第 333 级楼梯,它可以由第 222 级或第 111 级走到,即 dp[3]=2dp[3]=2dp[3]=2 .
对于第 222 级楼梯,它只可以由第 111 级走到,即 dp[2]=1dp[2]=1dp[2]=1 .
对于第 111 级楼梯,它是起始点,不需要由谁走到,即 dp[1]=0dp[1]=0dp[1]=0 .

由此我们可以推断出:dp[i]={0,i=11,i=22,i=3dp[i−1]+dp[i−2],i≥4dp[i]=\begin{cases}0, i=1 \\ 1, i=2 \\ 2, i=3 \\ dp[i-1] + dp[i-2] , i≥4\end{cases}dp[i]=⎩⎪⎪⎪⎨⎪⎪⎪⎧​0,i=11,i=22,i=3dp[i−1]+dp[i−2],i≥4​ .

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=110;int N,M,dp[maxn];void init()
{dp[1]=0,dp[2]=1,dp[3]=2;for(int i=4;i<maxn;i++){dp[i]=dp[i-1]+dp[i-2];}return ;
}int main()
{scanf("%d",&N);init();while(N--){scanf("%d",&M);printf("%d\n",dp[M]);}return 0;
}

B - 一只小蜜蜂… (HDU-2044)

有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。

Input
输入数据的第一行是一个整数N,表示测试实例的个数,然后是 N 行数据,每行包含两个整数a和b(0<a<b<50)。
Output
对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。

Sample Input Sample Output
2
1 2
3 6
1
3

分析

观察蜂房的布局,我们发现它是有规律的——上一行都是奇数,下一行都是偶数;每行当中两个元素之差与另一行对应的两个元素之差都相等;等等——我们就可以想到,不同起点到不同终点的距离如果相等,就说明它们的路线数都是相等的。
举波栗子:
1→22→33→44→5……的路线数都是 111 .
1→32→43→54→6……的路线数都是 222 .
1→42→53→64→7……的路线数都是 333 .(以1→4为例,路线为 ①1-2-3-4;②1-2-4;③1-3-4)
1→52→63→74→8……的路线数都是 555 .(以1→5为例,路线为 ①1-2-3-4-5;②1-2-3-5;③1-2-4-5;④1-3-4-5;⑤1-3-5)
……
举出足够多的栗子后(或者找规律比较秀),我们可以找到规律:对于起终点相差的距离 iii ,路线数为 dp[i]={1,i=12,i=2dp[i−1]+dp[i−2],i≥3dp[i]=\begin{cases}1, i=1 \\ 2, i=2 \\ dp[i-1] + dp[i-2] , i≥3\end{cases}dp[i]=⎩⎪⎨⎪⎧​1,i=12,i=2dp[i−1]+dp[i−2],i≥3​ .

注意要用long long。(因为斐波那契数增长得非常快)

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=110;int N,a,b;
ll dp[maxn];void init()
{dp[1]=1,dp[2]=2;for(int i=3;i<maxn;i++){dp[i]=dp[i-1]+dp[i-2];}return ;
}int main()
{scanf("%d",&N);init();while(N--){scanf("%d%d",&a,&b);printf("%lld\n",dp[b-a]);}return 0;
}

C - 母牛的故事 (HDU-2018)

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
Input
输入数据由多个测试实例组成,每个测试实例占一行,包括一个整数n(0<n<55),n的含义如题目中描述。
n=0表示输入数据的结束,不做处理。
Output
对于每个测试实例,输出在第n年的时候母牛的数量。
每个输出占一行。

Sample Input Sample Output
2
4
5
0
2
4
6

分析

记得这道题之前也写过!然后还在奇怪明明说年初就有新牛,为啥第一年不是两头牛,第二年是三头牛……
所以这道题实际上我是看给的样例来推断情况的,如图:

由此我们可以推断出式子:dp[i]={1,i=12,i=23,i=34,i=4dp[i−1]+dp[i−3],i≥5dp[i]=\begin{cases}1, i=1 \\ 2, i=2 \\ 3, i=3 \\ 4, i=4 \\ dp[i-1] + dp[i-3] , i≥5\end{cases}dp[i]=⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧​1,i=12,i=23,i=34,i=4dp[i−1]+dp[i−3],i≥5​ .( dp[i−1]dp[i-1]dp[i−1] 是上一年有的牛数, dp[i−3]dp[i-3]dp[i−3] 是今年要新添的新牛数)

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=110;int N;
ll dp[maxn];void init()
{dp[1]=1,dp[2]=2,dp[3]=3,dp[4]=4;for(int i=5;i<maxn;i++){dp[i]=dp[i-1]+dp[i-3];}return ;
}int main()
{init();while(~scanf("%d",&N)){if(N==0) break;printf("%lld\n",dp[N]);}return 0;
}

D - Common Subsequence (POJ-1458)

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.
Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input Sample Output
abcfbc abfcab
programming contest
abcd mnp
4
2
0

分析

题意比较好理解,就是给定两个字符串,问你这两个字符串的最大公共子串的长度是多少。
为了解决这个问题,我们需要开二维的 dpdpdp 数组,表示第一个字符串前 iii 个字符和第二个字符串前 jjj 个字符的最大公共子序列长度,并把初值都赋成 000 ,如图所示:(以字符串 “abcd”“abcd”“abcd” 和 “abdc”“abdc”“abdc” 为例)

这里把字符串都往后挪了一个位置,是因为这样写之后推公式比较方便,可以不考虑越界问题随意 −1-1−1 。但是比较字符串元素是否相等的时候,还是需要写成 str[i−1]str[i-1]str[i−1] 的形式,以免出现错误。最后,问题就变成了求 dp[len1][len2]dp[len1][len2]dp[len1][len2] 的值。

探讨状态转移方程的推导(怎么求子问题 dp[i][j]dp[i][j]dp[i][j] )如图:

  1. str1[i−1]=str2[j−1]str1[i-1]=str2[j-1]str1[i−1]=str2[j−1] 时
    如果 str1[i−1]=str2[j−1]str1[i-1]=str2[j-1]str1[i−1]=str2[j−1] ,那么当前最大公共子序列的长度为 “str1[0],str1[1],⋯,str1[i−2]”“str1[0],str1[1],\cdots,str1[i-2]”“str1[0],str1[1],⋯,str1[i−2]” 与 “str2[0],str2[1],⋯,str2[i−2]”“str2[0],str2[1],\cdots,str2[i-2]”“str2[0],str2[1],⋯,str2[i−2]” 的最长公共子序列的长度 +1+1+1 ,即 dp[i][j]=dp[i−1][j−1]+1dp[i][j]=dp[i-1][j-1]+1dp[i][j]=dp[i−1][j−1]+1 。
    以图中给出的栗子为例:
    当 i=1,j=1i=1,j=1i=1,j=1 时,字符串 “a”“a”“a” 与 “a”“a”“a” 比较,“a”“a”“a” 与 “a”“a”“a” 相等, dp[1][1]=dp[0][0]+1=1dp[1][1]=dp[0][0]+1=1dp[1][1]=dp[0][0]+1=1 .
    当 i=2,j=2i=2,j=2i=2,j=2 时,字符串 “ab”“ab”“ab” 与 “ab”“ab”“ab” 比较,“b”“b”“b” 与 “b”“b”“b” 相等, dp[2][2]=dp[1][1]+1=2dp[2][2]=dp[1][1]+1=2dp[2][2]=dp[1][1]+1=2 .
    当 i=3,j=4i=3,j=4i=3,j=4 时,字符串 “abc”“abc”“abc” 与 “abdc”“abdc”“abdc” 比较,“c”“c”“c” 与 “c”“c”“c” 相等, dp[3][4]=dp[2][3]+1=3dp[3][4]=dp[2][3]+1=3dp[3][4]=dp[2][3]+1=3 .
    ……
  2. str1[i−1]≠str2[j−1]str1[i-1]≠str2[j-1]str1[i−1]​=str2[j−1] 时
    str1[i−1]≠str2[j−1]str1[i-1]≠str2[j-1]str1[i−1]​=str2[j−1] ,那么当前最大公共子序列的长度为 max(“str1[0],str1[1],⋯,str1[i−2]”max(“str1[0],str1[1],\cdots,str1[i-2]”max(“str1[0],str1[1],⋯,str1[i−2]” 与 “str2[0],str2[1],⋯,str2[i−1]”,str1[0],str1[1],⋯,str1[i−1]”“str2[0],str2[1],\cdots,str2[i-1]” ,str1[0],str1[1],\cdots,str1[i-1]”“str2[0],str2[1],⋯,str2[i−1]”,str1[0],str1[1],⋯,str1[i−1]” 与 “str2[0],str2[1],⋯,str2[i−1]”)“str2[0],str2[1],\cdots,str2[i-1]”)“str2[0],str2[1],⋯,str2[i−1]”)的最长公共子序列的长度,即 dp[i][j]=max(dp[i−1][j],dp[i][j−1])dp[i][j]=max(dp[i-1][j],dp[i][j-1])dp[i][j]=max(dp[i−1][j],dp[i][j−1]) 。
    以图中给出的栗子为例:
    当 i=1,j=2i=1,j=2i=1,j=2 时,字符串 “a”“a”“a” 与 “ab”“ab”“ab” 比较,“a”“a”“a” 与 “b”“b”“b” 不相等, dp[1][2]=max(dp[0][2],dp[1][1])=1dp[1][2]=max(dp[0][2],dp[1][1])=1dp[1][2]=max(dp[0][2],dp[1][1])=1 。此时的最长公共子序列长度是 111 ,公共子序列是 “a”“a”“a” .
    当 i=2,j=1i=2,j=1i=2,j=1 时,字符串 “ab”“ab”“ab” 与 “a”“a”“a” 比较,“b”“b”“b” 与 “a”“a”“a” 不相等, dp[2][1]=max(dp[1][1],dp[2][0])=1dp[2][1]=max(dp[1][1],dp[2][0])=1dp[2][1]=max(dp[1][1],dp[2][0])=1 。此时的最长公共子序列长度是 111 ,公共子序列是 “a”“a”“a” .
    当 i=3,j=3i=3,j=3i=3,j=3 时,字符串 “abc”“abc”“abc” 与 “abd”“abd”“abd” 比较,“c”“c”“c” 与 “d”“d”“d” 不相等, dp[3][3]=max(dp[2][3],dp[3][2])=2dp[3][3]=max(dp[2][3],dp[3][2])=2dp[3][3]=max(dp[2][3],dp[3][2])=2 。此时的最长公共子序列长度是 222 ,公共子序列是 “ab”“ab”“ab” .
    ……

代码

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1010;int len1,len2,dp[maxn][maxn];
string str1,str2;int main()
{while(cin>>str1>>str2){len1=str1.size();len2=str2.size();memset(dp,0,sizeof(dp));for(int i=1;i<=len1;i++) {for(int j=1;j<=len2;j++){if(str1[i-1]==str2[j-1]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}}printf("%d\n",dp[len1][len2]);}return 0;
}

E - Longest Ordered Subsequence (POJ-2533)

A numeric sequence of ai is ordered if a1 < a2 < … < aN. Let the subsequence of the given numeric sequence (a1, a2, …, aN) be any sequence (ai1, ai2, …, aiK), where 1 <= i1 < i2 < … < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
Input
The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000
Output
Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.

Sample Input Sample Output
7
1 7 3 5 9 4 8
4

分析

题目写了一长串,实际上是问你给定序列中的最长上升子序列(严格上升)的长度是多少。

先读入原序列数据,存放到 aaa 数组中。然后可以在读入的过程中统计最长上升子序列的长度,也可以读完后再统计,这里是边读边记的做法(因为这样快点)。

dp[i]dp[i]dp[i] 表示以第 iii 个元素作结的最长上升子序列的长度,那么对于每个 iii ,它的 dp[i]dp[i]dp[i] 的初值都是 111 (一个数本身就是递增序列)。
对于 j<ij<ij<i ,如果 a[j]<a[i]a[j]<a[i]a[j]<a[i] ,说明 a[j]a[j]a[j] 和 a[i]a[i]a[i] 是一小段上升子序列。此时应该比较 dp[i]dp[i]dp[i] 与 dp[j]+1dp[j]+1dp[j]+1 的大小。如果 dp[i]>dp[j]+1dp[i]>dp[j]+1dp[i]>dp[j]+1 ,说明之前存放的以 a[i]a[i]a[i] 作为结尾的子序列比现在碰见的更长,因此不做改变;如果 dp[i]<dp[j]+1dp[i]<dp[j]+1dp[i]<dp[j]+1 ,说明之前存放的以 a[i]a[i]a[i] 作为结尾的子序列比现在碰见的更短,因此更新 dp[i]dp[i]dp[i] 的值为 dp[j]+1dp[j]+1dp[j]+1 。
最后用 resresres 存放序列中最长上升序列的长度,并在循环结束后输出其结果。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1010;int N,a[maxn],dp[maxn],res;int main()
{scanf("%d",&N);res=1;for(int i=1;i<=N;i++){scanf("%d",&a[i]);dp[i]=1;for(int j=1;j<i;j++){if(a[j]<a[i])  {dp[i]=max(dp[i],dp[j]+1);}}res=max(res,dp[i]);}printf("%d",res);return 0;
}

F - Super Jumping! Jumping! Jumping! (HDU-1087)

Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. Maybe you are a good boy, and know little about this game, so I introduce it to you now.
The game can be played by two or more than two players. It consists of a chessboard(棋盘)and some chessmen(棋子), and all chessmen are marked by a positive integer or “start” or “end”. The player starts from start-point and must jumps into end-point finally. In the course of jumping, the player will visit the chessmen in the path, but everyone must jumps from one chessman to another absolutely bigger (you can assume start-point is a minimum and end-point is a maximum.). And all players cannot go backwards. One jumping can go from a chessman to next, also can go across many chessmen, and even you can straightly get to end-point from start-point. Of course you get zero point in this situation. A player is a winner if and only if he can get a bigger score according to his jumping solution. Note that your score comes from the sum of value on the chessmen in you jumping path.
Your task is to output the maximum value according to the given chessmen list.
Input
Input contains multiple test cases. Each test case is described in a line as follow:
N value_1 value_2 …value_N
It is guarantied that N is not more than 1000 and all value_i are in the range of 32-int.
A test case starting with 0 terminates the input and this test case is not to be processed.
Output
For each case, print the maximum according to rules, and one line one case.

Sample Input Sample Output
3 1 3 2
4 1 2 3 4
4 3 3 2 1
0
4
10
3

分析

这题和上一题差不多,只不过题意变成了求给定序列中的最大上升子序列和。而 dp[i]dp[i]dp[i] 的初值不是 111 而是每个元素的值 a[i]a[i]a[i] ,且发现 a[j]<a[i]a[j]<a[i]a[j]<a[i] 后, dp[i]dp[i]dp[i] 存放的也是 dp[i]dp[i]dp[i] 和 dp[j]+a[i]dp[j]+a[i]dp[j]+a[i] 中的较大值。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1010;int N,a[maxn],dp[maxn],res;int main()
{while(~scanf("%d",&N)){if(N==0)    break;res=0;memset(a,0,sizeof(a));memset(dp,0,sizeof(dp));for(int i=1;i<=N;i++){scanf("%d",&a[i]);}for(int i=1;i<=N;i++){dp[i]=a[i];  //如果之前的元素都比a[i]大,a[i]就自己形成序列,和为a[i]for(int j=1;j<i;j++){if(a[j]<a[i])  {dp[i]=max(dp[i],dp[j]+a[i]);}}}for(int i=1;i<=N;i++){res=max(res,dp[i]); //找到子序列中的最大和}printf("%d\n",res);}return 0;
}

G - Jumping Cows (POJ-2181)

Farmer John’s cows would like to jump over the moon, just like the cows in their favorite nursery rhyme. Unfortunately, cows can not jump.
The local witch doctor has mixed up P (1 <= P <= 150,000) potions to aid the cows in their quest to jump. These potions must be administered exactly in the order they were created, though some may be skipped.
Each potion has a ‘strength’ (1 <= strength <= 500) that enhances the cows’ jumping ability. Taking a potion during an odd time step increases the cows’ jump; taking a potion during an even time step decreases the jump. Before taking any potions the cows’ jumping ability is, of course, 0.
No potion can be taken twice, and once the cow has begun taking potions, one potion must be taken during each time step, starting at time 1. One or more potions may be skipped in each turn.
Determine which potions to take to get the highest jump.
Input
* Line 1: A single integer, P
* Lines 2…P+1: Each line contains a single integer that is the strength of a potion. Line 2 gives the strength of the first potion; line 3 gives the strength of the second potion; and so on.
Output
* Line 1: A single integer that is the maximum possible jump.

Sample Input Sample Output
8
7
2
1
8
4
3
5
6
17

分析

题意:给定 PPP 个数字,从前往后开始取数。一开始初值为 000 ,奇数次操作加上取的数,偶数次操作减去取的数,问最后最大结果是多少。

这道题有两种做法,一个是贪心,一个是 dpdpdp 。