经典线性dp
1.最长上升子序列 (昨天飞机那道题)
问题 A: 导弹袭击
时间限制: 1 Sec 内存限制: 128 MB
题目描述
某国为了防御敌国的导弹袭击发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度但是以后每一发炮弹都不能超过前一发的高度.某天雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段所以只有一套系统因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易成本呢?成本是个大问题啊.所以俺就到这里来求救了请帮助计算一下最少需要多少套拦截系统.
输入
输入若干组数据.每组数据包括:导弹总个数(正整数)导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数用空格分隔)
输出
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
样例输入
8 389 207 155 300 299 170 158 65
样例输出
2
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000;
int num[maxn],dp[maxn];
int main(){
int n;
while(cin>>n){
for(int i=1;i<=n;i++){
cin>>num[i];
dp[i]=1;
}
int Max=0;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(num[i]>num[j]&&dp[j]+1>dp[i]){
dp[i]=dp[j]+1;
}
Max=max(Max,dp[i]);
}
}
// for(int i=1;i<=n;i++){
// cout<<dp[i]<<" ";
// }
// cout<<endl;
cout<<Max<<endl;
}
}
问题 F: 红红跳格子
时间限制: 1 Sec 内存限制: 128 MB
题目描述
红红在上体育课。今天体育老师不在,红红就有时间和同学们一起玩游戏了。
突然,红红发现,红红的面前有n个方格,每个方格上面写了一个数字。
红红突然想起了儿时的“跳房子”游戏。可是作为大学生,怎么能玩这么…低龄…的游戏呢!于是决定树立一个新的规则。
红红可以从方格外开始起跳。他每跳到一个格子上,就可以获得格子上数字那么多钱!但是他下一步跳到的格子上的数字必须比当前格子上的数字更大。
红红慌了,不知道他能拿到的钱是多少。聪明的你可以帮他计算一下吗?
输入
输入包含多组测试样例。每一个测试样例被如下描述:
N v1 v2 … vN
保证对于每一个测试样例,N ≤ 1000,并且 vi 在32位整数范围内。
以0开头的测试样例作为输入终止标志,并且这组样例不被处理
输出
对于每一个测试样例,输出根据规则他最多可以获得的金额,每个测试样例一行。
样例输入
3 1 3 2
5 1 1 2 3 4
4 3 2 1 1
0
样例输出
4
10
3
这个跟上面的一样,不过加1变为加他的权值num[i]
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000+5;
int dp[maxn],num[maxn];
int main(){
int n;
while(cin>>n){
if(n==0){
break;
}
memset(dp,0,sizeof(dp));
int Max=0;
for(int i=1;i<=n;i++)
cin>>num[i];
for(int i=1;i<=n;i++){
dp[i]=num[i];
for(int j=1;j<i;j++){
if(num[j]<num[i]){
dp[i]=max(dp[i],dp[j]+num[i]);
}
}
Max=max(Max,dp[i]);
}
// for(int i=1;i<=n;i++)
// cout<<dp[i]<<" ";
// cout<<endl;
cout<<Max<<endl;
}
}
2.最长公共子序列
问题 B: Common Subsequence
时间限制: 1 Sec 内存限制: 128 MB
题目描述
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 = 12…k x ij = 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.
输入
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 = 12…k x ij = 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.
输出
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.
样例输入
abcfbc abfcab
programming contest
abcd mnp
样例输出
4
2
0
翻译
题目描述
给定序列的子序列是给定序列,其中省略了一些元素(可能没有)。给定序列X = <x1 x2 … xm>如果存在严格增加的X的索引<i1 i2 … ik>,则另一个序列Z = <z1 z2 … zk>是X的子序列。对于所有j = 12 … kx ij = zj。例如,Z = 是X = 的子序列,索引序列<1 2 4 6>。给定两个序列X和Y,问题是找到X和Y的最大长度公共子序列的长度。
输入
给定序列的子序列是给定序列,其中省略了一些元素(可能没有)。给定序列X = <x1 x2 … xm>如果存在严格增加的X的索引<i1 i2 … ik>,则另一个序列Z = <z1 z2 … zk>是X的子序列。对于所有j = 12 … kx ij = zj。例如,Z = 是X = 的子序列,索引序列<1 2 4 6>。给定两个序列X和Y,问题是找到X和Y的最大长度公共子序列的长度。
输出
对于每组数据,程序在标准输出上打印从单独行开始的最大长度公共子序列的长度。
#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int main(){
while(cin>>s1>>s2){
if(s1.size()>s2.size()){
string temp;
temp=s1;
s1=s2;
s2=temp;
}//s1是短的
int dp[s2.size()+5][s2.size()+5];
memset(dp,0,sizeof(dp));
int Max=0;
for(int i=0;i<s2.size();i++){
int k=i+1;
for(int j=0;j<s1.size();j++){
int l=j+1;
if(s2[i]==s1[j])
dp[k][l]=dp[k-1][l-1]+1;
else
dp[k][l]=max(dp[k-1][l],dp[k][l-1]);
Max=max(Max,dp[k][l]);
}
}
// for(int i=1;i<=s2.size();i++){
// for(int j=1;j<=s1.size();j++)
// cout<<dp[i][j]<<" ";
// cout<<endl;
// }
cout<<Max<<endl;
}
}
3.最大连续子序列和
问题 C: 滑雪
时间限制: 1 Sec 内存限制: 128 MB
题目描述
Michael喜欢滑雪这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道在一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
输入
输入的第一行表示区域的行数R和列数C(1 <= RC <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
输出
输出最长区域的长度。
样例输入
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出
25
dfs+动态规划
#include<bits/stdc++.h>
using namespace std;
const int maxn=100+5;
int dp[maxn][maxn];
int Map[maxn][maxn];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int n,m;
int dfs(int x,int y){
int nextX,nextY;
int Max=0;
if(dp[x][y]!=0)//如果进来这个点已经存在以i,j为起点的最大长度
return dp[x][y]; //那我直接衔接上就行
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
nextX=x+dx[i];
nextY=y+dy[i];
if(nextX<1||nextY<1||nextX>n||nextY>m)
continue;
if(Map[nextX][nextY]<Map[x][y]){
Max=max(Max,dfs(nextX,nextY));
}
}
}
dp[x][y]=Max+1; //末状态走不了
return dp[x][y];
}
int main(){
while(cin>>n>>m){
memset(dp,0,sizeof(dp));
memset(Map,0,sizeof(Map));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>Map[i][j];
int Max=0,length;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
length=dfs(i,j);
Max=max(Max,length);
}
}
cout<<Max<<endl;
}
}
区间dp
在一段区间上的动态规划
既要满足dp问题的最优子结构和无后效性外,还应该符合在区间上操作的特点
往往会对区间进行合并操作,或是单个元素(可看成一个小区间)跨区间进行操作。
下面是模板代码
区间合并操作
有 N 堆石子排成一排,每堆石子有一定的数量。相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
:例输入
3
1 2 3
输出
9
注 8 4 6 3 5 按照贪心是62 但最优解是60…… 贪心满足局部最优并不一定全局最优
1~n区间有一个状态,n-1个决策,
长度为n-2区间有两个状态,n-2个决策,
.......
长度为1区间有n-1个状态,1个决策
决策,就比如长度为n是的dp就要在我长度为n-1的区间里选一个最大的状态再加上我这个区间的sum
#include<bits/stdc++.h>
using namespace std;
const int maxn=200+5;
int num[maxn],dp[maxn][maxn],sum[maxn][maxn];
//dp[i,j]表示i到j合并的所有石子的得分
//sum[i][j]表示i到j合并的石子的价值和
int main(){
int n;
while(cin>>n){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
cin>>num[i];
for(int i=1;i<=n;i++){
sum[i][i]=num[i];
for(int j=i+1;j<=n;j++){
sum[i][j]=sum[i][j-1]+num[j];
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++)
// cout<<sum[i][j]<<" ";
// cout<<endl;
// }
for(int len=2;len<=n;len++){
for(int i=1;i<=n;i++){
int j=i+len-1;
dp[i][j]=0x3f3f3f3f; //边界初始条件令为最大
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]+sum[i][j]);
}
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++)
// cout<<dp[i][j]<<" ";
// cout<<endl;
// }
cout<<dp[1][n]<<endl;
}
}
问题 D: 小红红益智游戏
时间限制: 1 Sec 内存限制: 128 MB
提交: 132 解决: 52
[提交][状态][讨论版][命题人:winsoul]
题目描述
小红红非常喜欢数论,但是他对一些问题很不解。
例如有一次,他在幼儿园的操场上把 N 堆石子摆成了一排。(每堆石子都有一定的数量。)
小红红想,如果每次合并两堆石子的得分就等于这两堆石子的和。
把一排的 N 堆石子合并成一堆可以的得到的最高分是多少呢?
每次只能合并相邻的两堆,每次合并完成后,新的一堆石子和两边的石子又会形成新的相邻关系。
输入
输入包含多组测试数据。
对于每组测试数据:
第一行输入为 N ,表示一排上有 N 堆石子。(0 < N < 200)
第二行输入为 N 个正整数,代表第 i 堆石子的数量。(0 < ni < 100000)
输出
输出总得分的最大值。每个测试样例输出占一行。
样例输入
4
4 5 9 4
样例输出
54
这个和上面的石子略有不同,这个是求最大得分,那个是求最小代价和
#include<bits/stdc++.h>
using namespace std;
const int maxn=200+5;
int num[maxn],dp[maxn][maxn],sum[maxn][maxn];
int main(){
int n;
while(cin>>n){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
cin>>num[i];
for(int i=1;i<=n;i++){
sum[i][i]=num[i];
for(int j=i+1;j<=n;j++){
sum[i][j]=sum[i][j-1]+num[j];
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++)
// cout<<sum[i][j]<<" ";
// cout<<endl;
// }
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]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j]);
}
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n;j++)
// cout<<dp[i][j]<<" ";
// cout<<endl;
// }
cout<<dp[1][n]<<endl;
}
}
元素跨区间操作
如括号匹配,后面的例题
问题 E: 最大括号匹配数
时间限制: 1 Sec 内存限制: 128 MB
提交: 81 解决: 34
[提交][状态][讨论版][命题人:winsoul]
题目描述
已知有两种括号匹配类型,分别是‘(’和‘)’,‘[’和‘]’。
给定一个字符串,只包含这 4 种符号,长度不超过100。
求出可以匹配括号的最大数量。
输入
输入有多组测试数据,输入0时结束。
每组测试样例只有一行,只包含 ’ ( ’ ,’ ) ', ’ [ ', ’ ] ’ 四种符号,且长度不超过一百
输出
每组测试样例输出一行,结果为可以匹配括号的最大数目。
样例输入
((()))
()()()
()))()
([()])
0
样例输出
6
6
4
6
#include<bits/stdc++.h>
using namespace std;
const int maxn=100+5;
int dp[maxn][maxn];
string s;
int check(int i,int j){
if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']')) return 1;
return 0;
}
int main(){
while(cin>>s){
if(s=="0") break;
memset(dp,0,sizeof(dp));
for(int len=1;len<s.size();len++){
for(int i=0;i<s.size();i++){
int j=i+len;
if(j>s.size()-1) break;
if(check(i,j)){
if(i+1>j-1)
dp[i][j]=2;
else
dp[i][j]=dp[i+1][j-1]+2;
}
for(int k=i;k<j;k++){
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
}
}
// for(int i=0;i<s.size();i++){
// for(int j=0;j<s.size();j++)
// cout<<dp[i][j]<<" ";
// cout<<endl;
// }
cout<<dp[0][s.size()-1]<<endl;
}
}
其他dp题
问题 G: 请客的红红
时间限制: 1 Sec 内存限制: 128 MB
题目描述
有一天红红拿了奖学金,与此同时,红红说好了要请集训的所有同学去看电影,现在红红就要负责去购买电影票,现在电影票可以一个一个的购买,也可以两个人一起购买,如果知道每个人单独买票花费的时间,还有和前一个人一起买花费的时间,问最少花多长时间可以全部买完票。
输入
给出 N(1<=N<=10),表示有N组样例
给出K (1<=K<=2000),表示有K个人买票…
给出K个数表示这个人单独买票会花的时间…保证每个数 (0s<=Si<=35s)
给出K-1个数,表示这个人和前面那个人一起买票会花的时间…保证每个数 (0s<=Si<=70s)
输出
对于每一组数据,你需要给出电影院售票结束的时间,
售票开始的时间为 08:00:00 am. 时间格式为: HH:MM:SS am 或者 pm.
具体看样例输出
样例输入
2
2
20 25
40
1
8
样例输出
08:00:40 am
08:00:08 am
#include<bits/stdc++.h>
using namespace std;
const int maxn=2000+5;
int solo[maxn],together[maxn],dp[maxn];
int main(){
int n;
cin>>n;
while(n--){
memset(dp,0,sizeof(dp));
memset(together,0,sizeof(together));
memset(solo,0,sizeof(solo));
int k;
cin>>k;
for(int i=1;i<=k;i++){
cin>>solo[i];
dp[i]=solo[i];
}
for(int i=2;i<=k;i++)
cin>>together[i];
for(int i=2;i<=k;i++){ //第一个人绝对是自己一个人买的,所以dp[1]=solo[1],从2开始
dp[i]=min(dp[i-1]+solo[i],dp[i-2]+together[i]);//判断一个人买时间少还是两个人买时间少
}
int h=dp[k]/3600+8;
int m=dp[k]/60%60;
int s=dp[k]%60;
if(h<=12)
printf("%02d:%02d:%02d am\n",h,m,s);
else
printf("%02d:%02d:%02d pm\n",h,m,s);
}
return 0;
}