动态规划水题

1.二维数组最小和
M*N维的二维数组,每个位置有一个非负值,每次只能从右或者从下走,求从(1,1)走到(M,N)的最小值
简单dp,开始的时候设置边界为INF
枚举:C(m+n-2,m)(其中有m步向下走)

dp[i][j]=min(dp[i-1][j],dp[i][j-1])+dis[i][j];


2.最大子数组和
一个整数数组长N,求一个非空的连续子数组使得它的和最大
枚举:开头结尾位置(i,j)一共N*N种,但是还要求和,就是O(N^3)
分治:O(nlogn)
在线更新:dp[i]表示以i结尾的最大子数组和(以结尾是哪个为标准)
选择a[i]和不选择a[i]
dp[i]=max(a[i],dp[i-1]+a[i]);
3.硬币问题
硬币面值为C1,C2,C3。。硬币数目不限,组成一定的钱
类似完全背包问题:面值Ci用或者不用
for(int i=1;i<=N;i++){
for(int j=1;j<=V;j++){
if(j>=C[i])
dp[i][j]=min(dp[i-1][j],dp[i-1][j-C[i]]+1);
else dp[i][j]=dp[i-1][j-1];
}
}


4.0-1背包:复杂度O(n*W)
for(int i=1;i<=N;i++){
for(int j=W;j>=1;j++)
if(j>=w[i])
dp[i]=max(dp[i],dp[i-w[i]]+v[i]);
}

  完全背包:复杂度O(N*w)
for(int i=1;i<=N;i++){
for(int j=1;j<=W;j++)
if(j>=w[i])
dp[i]=max(dp[i],dp[i-w[i]]+v[i]);
}
为什么是O(n*W)?因为是用背包重量和物品数量来当做计数元
01背包问题之2 
如果卡时间,O(n*W)超时,那么可以用价格来计数,同样价格的最小重量。实质是一样的。都是最优子结构
dp[i][j]//i denotes items
// j denotes value
//if not INF
for(int i=1;i<=N;i++)
for(int j=Maxn*Maxv;j>=1;j--)
if(j>v[i]);
dp[i][j]=min(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
5.钢管切割问题:算导P205
和硬币问题一样,长度变成了价值,每个长度对应的价格变成了货币
for(int i=1;i<=N;i++){
for(int j=1;j<=L;j++){
if(j>i)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-i]+v[i]);
else do[i][j]=dp[i-1][j];
}
}
dp[i][j]使用了前i种货币价值,切下来j的长度


6.最短编辑距离
设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作包括:(1)删除一个字符;(2)插入一个字符;(3)将一个字符改为另一个字符。将字符串A变换为字符串B所用的最少字符操作数称为字符串A到B的编辑距离,记为d(A,B)。试设计一个有效算法,对任给的2个字符串A和B,计算出它们的编辑距离d(A,B)。要求:输入:第1行是字符串A,第2行是字符串B。输出:字符串A和B的编辑距离d(A,B)
POJ 3356
有点类似LCS

但是不能用LCS来算,如果当前a[i]==b[j],不能保证前面没有更小的k<j使得a[i]==b[j]

解题思路:类似于最长公共子串
我们设dp[i][j]的意义为y取前i个字母和x取前j个字母的最少操作次数

注意要从小的开始分析,这是边界情况,边界不一定全是0,小心

那么可以得到dp[0][i] = i和dp[i][0]=i,因为某一字符串为空的,要得到另一个i长度字符串,必须经过i次插入操作。
而dp[1][1],有3中操作,
1.转换 ,将str1[0]和str2[0]判断,如果相等,则dp[1][1]=0,否则dp[1][1]=1
2.删除,因为,目的串比源串小,所以删除源串一个字符,
也就是必须有一次操作,删除str1[0]后,那么dp[1][1]就是dp[0][1]的值+1
3.添加,在目的串添加一个字符,即源串不变,但是目的串减1,和源串去匹配,即dp[1][0] + 1

充分考虑三种操作,只有三种状态转移,可以不假思索的写出来的:

这样dp[i][j]可以得到3中操作的最小值
dp[i-1][j-1]+str1[i]==str2[j]?0:1
dp[i-1][j]+1
dp[i][j-1]+1


注意下标I,J,老是写错啊啊啊啊啊啊a[i]==b[j]

#include <iostream>
#include <algorithm>
int min3(int a,int b,int c){
	if (a>b) a=b;
	if (a>c) a=c;
	return a;
}
using namespace std;
int N,M;
const int maxn=10005;
char a[maxn],b[maxn];
int dp[maxn][maxn];
int main(int argc, char const *argv[])
{
	cin>>N;
	for(int i=1;i<=N;i++){
		cin>>a[i];
		dp[i][0]=i;
	}
	cin>>M;
	for(int j=1;j<=M;j++){
		cin>>b[j];
		dp[0][j]=j;
	}//M>=N
	for(int i=1;i<=N;i++){
		for(int j=1;j<=M;j++){
			dp[i][j]=min3(
				dp[i-1][j-1]+(a[i]==b[j]?0:1),
				dp[i-1][j]+1,
				dp[i][j-1]+1);
		//	cout<<dp[i][j]<<" ";
		}
	//cout<<endl;
	}
	// cout<<"  ";
	// for(int i=1;i<=M;i++){
	// 	cout<<b[i]<<" ";
	// }
	// cout<<endl;
	// for(int i=1;i<=N;i++){
	// 	cout<<a[i]<<" ";
	// 	for(int j=1;j<=M;j++)
	// 		cout<<dp[i][j]<<" ";
	// 	cout<<endl;
	// }
	// cout<<endl;
	cout<<dp[N][M]<<endl;;

	/* code */
	return 0;
}
朱神的最大最小值写法:

维护性质 a是a,b中最小的 a是a,c中最小的

int min3(int a,int b,int c){
	if (a>b) a=b;
	if (a>c) a=c;
	return a;
}




7.石子合并问题
石子合并问题是最经典的DP问题。首先它有如下3种题型:
(1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。
分析:构造哈夫曼树
朱神提携:动态DP

(2)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。
动态规划最主要是要满足最优子结构,这里的最优子结构dp[i][j]i到j之间最小的

dp[i][j]=min(dp[i][i+k],dp[i+k][k])(k=1,2,3。。。i-j-1);

O(N^3)

主要是要想到这里的最优子结构是i到j之间的最小的,这是灵活的处理,不能死板从1到N

8.矩阵连乘:和石子合并几乎一样

两个矩阵连乘:A(m*n) B(n*p)

复杂度运算量(m*n*p)

  题目描述:给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2 ,…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。例如:

  A1={30x35} ; A2={35x15} ;A3={15x5} ;A4={5x10} ;A5={10x20} ;A6={20x25} ;

最后的结果为:((A1(A2A3))((A4A5)A6))  最小的乘次为15125。

dp[i][j]表示i到j的最小运算量

给定的条件应该是两个数字L[maxn],R[maxn],表示矩阵i有L[i]行,R[i]列

dp[i][j]=min{dp[i][i+k]+dp[i+k][j]+L[i]*R[j]*L[k]} (L[k]可以用R[k+1])

<pre name="code" class="cpp">

 
 

 
 


9.多重背包问题

有N种物品和一个容量为V的背包。第i种物品最多有n件可用,每件体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

方法1:转换成0-1背包,看成N[i]*n件商品的0-1背包:过于复杂
方法2:

你可能感兴趣的:(动态规划水题)