以前做过的几道常用DP 问题集合(以备以后记忆)
来源:互联网 发布:python或和逻辑运算符 编辑:程序博客网 时间:2024/04/28 06:23
动态规划(石子并归问题)
题意:
有 n 个石子,把n 堆石子合并成一个,合并时候只能是相邻的两个石子,合并时能获得积分为合并两队石子的数目之和,问如何合并能或得最多或者最小的积分:
例如:4 个石子
输入:4 4 5 9 (为每一堆的石子个数,如果想让1 2 堆合并则得到积分 8)
输出 43 54
解题思路:
令f[i:j] 表示把第i 堆到 到第 j 堆合并得到的积分这有
f[i:j]={ f[i:k]+f[k+1][j]} (其中 k>=i k<j)
令max[i:j] 为表示把第i 堆到 到第 j 堆合并得到的积分最大值(或者最小值)有:
max[i:j]=max{ f[i:j]+max[i:k]+max[k+1:j];
其实这这类型的题目和矩阵相乘、能量项链的题目都是类似的
#include<iostream>#define Max 100using namespace std;int a[Max];int p[Max][Max];//记录f[i:j]int max[Max][Max];//int s[Max][Max];// 记录为void sovle_max(int n){// memset(p,0,sizeof(p)); memset(max,0,sizeof(max)); for(int i=1;i<=n;i++){ p[i][i]=a[i]; } for( int r=2;r<=n;r++){ for(int i=1;i<=n-r+1;i++){ int j=i+r-1; int temp=0; for(int k=i;k<j;k++){ if(p[i][k]+p[k+1][j]+max[i][k]+max[k+1][j]>temp){ temp=p[i][k]+p[k+1][j]+max[i][k]+max[k+1][j]; p[i][j]=p[i][k]+p[k+1][j]; s[i][j]=k; } } //p[i][j]=temp; max[i][j]=temp; } }}void sovle_min(int n){// memset(p,0,sizeof(p)); memset(max,0,sizeof(max)); for(int i=1;i<=n;i++){ p[i][i]=a[i]; } for( int r=2;r<=n;r++){ for(int i=1;i<=n-r+1;i++){ int j=i+r-1; int temp=(1<<30); for(int k=i;k<j;k++){ if(p[i][k]+p[k+1][j]+max[i][k]+max[k+1][j]<temp){ temp=p[i][k]+p[k+1][j]+max[i][k]+max[k+1][j]; p[i][j]=p[i][k]+p[k+1][j]; s[i][j]=k; } } //p[i][j]=temp; max[i][j]=temp; } }}void print(int n){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cout<<max[i][j]<<" "; } cout<<endl; }}int main(void){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } sovle_max(n); print(n); sovle_min(n); print(n);}
输入格式
接下来的N行,按排队的顺序,描述每个人支持的球队,用H或J表示。
输出格式
样例输入
H
注释
令F[i] 表示前i 个看球人的需要乘坐的最小车数,那么有 i=0 时,f[i]=0; 对第前 i 个人进行考虑:那么就是正当 第i 个人和 前1->i-1 的考虑。(0=<j<i) 那么当且有 当j+1
#include <iostream>#include <cmath>#define Max 2000using namespace std;int ds[Max];// 表示前面 i 个人中 H 和 J 的差char ch[Max];int f[Max];// 表示前 i 个人坐车需要的最小车辆int min(int a,int b){ return a>b?b:a;}void solve(int n,int d){ for(int i=1;i<=n;i++){ f[i]=(1<<30); for(int j=0;j<i;j++){ if(abs(ds[i]-ds[j]<=d) || abs(ds[i]-ds[j])==i-j){// 表示如果从从j+1 到 第i 个人能和前面 j 个人乘坐同一辆车 //f[i]=min(f[j],f[i]); if(f[j]+1<f[i]){ f[i]=f[j]+1; } } } }}int main(void){ int n,d; int hn,jn; cin>>n>>d; for(int i=1;i<=n;i++){ cin>>ch[i]; if(ch[i]=='H'){ hn++; }else{ jn++; } ds[i]=hn-jn; } solve( n,d); cout<<f[n]<<endl;}
动态规划(最长公共子序列问题)
题意:求两个字符串中的最长公共子序列,(字符数组 x[] 和 y[]表示)
思路: 假设 c[i][j] 表示的从x[]中 1->i 和y[] 中 1->y 的最长公共子序列的值,那么对 c[i][j] (1<=i<=n,1<=j<=n) 进行考虑,如果是 x[i]==y[j] 那么有 c[i][j]=c[i-1][j-1]+1; 如果 x[i]!=y[j] 那显然c[i][j] 必须选择最大的:有c[i][j]=max{ c[i-1][j],c[i][j-1]} ;在动态规划的时候除了写出方程式外,还有考虑到初始化的问题,以本题为例,显然有初始化条件 c[i][0]=0 c[0][j]=0;
另外提出变形问题:
比如字符串“Ab3bd”,在插入两个字符后可以变成一个回文词(“dAb3bAd”或“Adb3bdA”)。然而,插入两个以下的字符无法使它变成一个回文词。
输入格式
Ab3bd
样例输出
这个问题的思路其实也是一样的,就是把求字符串正序和逆序的最大最大公共子序列m,在将总共数n-m 就可以了。(代码省略)
代码如下:
#include<iostream>#include<string>#define Max 125using namespace std;void LCSLength(int m,int n,char *x,char*y,int **c){// for(int i=1;i<=m;i++){ c[i][0]=0; } for(int j=1;j<=n;j++){ c[0][j]=0; } for(i=1;i<=m;i++){ for(j=1;j<=n;j++){ if(x[i]==y[j]){ c[i][j]= c[i-1][j-1]+1; } else if(c[i-1][j]>c[i][j-1]){ c[i][j]=c[i-1][j]; } else c[i][j]=c[i][j-1]; } } cout<<c[m][n]<<endl;}int main(void){ string x1,y1; cin>>x1>>y1; int m=0,n=0; char x[Max],y[Max]; x[0]='0';y[0]='0'; m=x1.length(); n=y1.length(); for(int i=0;i<m;i++){ x[i+1]=x1[i]; } for(i=0;i<n;i++){ y[i+1]=y1[i]; } int **c; c=new int*[m+1]; for(i=0;i<=m;i++){ c[i]=new int[n+1]; for(int j=0;j<=n;j++){ c[i][j]=0; } } LCSLength(m, n,x,y,c);}
P5:滑雪
Time
Description
Michael喜欢滑雪百这并不奇怪,
16
15
14
13
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1
Output
输出最长区域的长度。
Sample Input
5
1
16
15
14
13
Sample Output
25
解题思路:主要要到深度优先搜索和备忘录法。如果点 map[i][j] 已经被搜索过,那么,在可以不经行搜索 。令 re[i][j] 为从i 点出发搜到的最长的路劲 那么有 re[i][j]=re_max+1; 其中re_max 表示是i:j 点四个方向的最长值。re_max=max{ re[xt][yt]}
如是有:
#include<iostream>#define Max 200using namespace std;int map[Max][Max];int re[Max][Max];int d[4][2]={{0,-1},{-1,0},{0,1},{1,0}};int flage[Max][Max];int dfs(int x,int y,int m,int n){ if(re[x][y]!=0){ return re[x][y]; } int re_max=0;// 表示四周的最大值 for(int i=0;i<4;i++){ int xt=x+d[i][0],yt=y+d[i][1]; if( xt<=0 || yt<=0 || xt>m || yt>n || flage[xt][yt]==1 || map[xt][yt]>=map[x][y]){ continue; } int temp=dfs(xt,yt,m,n); if(temp>re_max){ re_max=temp; } } return re[x][y]=re_max+1;}void solve(int m,int n){ for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ dfs(i,j,m,n); } }}int main(void){ int max=0; int m,n,i; cin>>m>>n; for(i=1;i<=m;i++){ for(int j=1;j<=n;j++){ cin>>map[i][j]; } } memset(re,0,sizeof(re)); solve(m,n); for(i=1;i<=m;i++){ for(int j=1;j<=m;j++){ if(re[i][j]>max){ max=re[i][j]; } } } cout<<max<<endl;}
- 以前做过的几道常用DP 问题集合(以备以后记忆)
- 做过的DP集合
- 忽然发现以前的做过的东西都忘记了,以后要注意多做做笔记了
- 以前做过的服务器技术演进
- 留点做过的题,以后用
- 一些以前没做的简单DP
- 做以前未做过的,感觉就是不一样!
- 做过的DFS集合
- 做过的BFS集合
- 做过的dp汇总
- 留着以后慢慢做的树形DP(题表)
- 留着以后慢慢做的DP(题表)
- 留着以后慢慢做的数位DP(题表)
- (转)嵌入式WEB服务器BOA的移植方法(以前开发过,留下以备后用)
- 100道动态规划——33 HDU 5602 Black Jack 博弈DP?以前没做过这个类型
- 留着以后慢慢做的DP
- 以前做过的几个垂直搜索网站
- 以前做过的一个任务,ECmall二次开发
- android 给图片添加圆角
- C# RC4 代码实现
- Outlook 插件开发小结
- 前端代码标准最佳实践:HTML篇
- 【js特效】一款不错的flash视频播放器
- 以前做过的几道常用DP 问题集合(以备以后记忆)
- [SQL Server] 从oracle迁移到SQL Server.
- 关于Only the original thread that created a view hierarchy can touch its views的解决方案
- 已知中序和后序求先序
- NDK详解
- VLC编译configure
- 敏捷模式中的隐喻 打比方形象趣味记忆
- MySQL监控应该知道的九件事
- 前台js 弹出 confirm 连接后台按钮方法