赞
踩
在用分治法求解时,有些问题被重复计算了好多次。如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算。为达到这个目的,可以用一个表来记录所有已解决的子问题的答案,只要他被计算过,就将其填入表中。
这种计算次序可以用加括号的方式来确定。若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
- public static void matrixMultiply(int [][]a,int [][]b,int [][]c,int ra,int ca,int rb,int cb){
- for (int i = 0;i<ra;i++){
- for (int j = 0;j<cb;j++){
- int sum = 0;
- for (int k = 0;k<ca;k++){
- sum = sum + a[i][k]*b[k][j];
- }
- c[i][j] = sum;
- }
- }
- }
计算3个矩阵{A1,A2,A3 }连乘积的例子。设这3个矩阵的维数分别为10 X 100,100 X 5和5 X 50。若按第一种加括号方式((A1,A2)A3)计算,3个矩阵连乘积需要的数乘次数为10 X 100 X 5 + 10 X 5 X 50 = 7500。若按第二种加括号方式(A1(A2A3))计算,3个矩阵连乘积总共需要10X5X50+ 10 X 100 X 50 = 75 000次数乘。第二种加括号方式的计算量是第一种加括号方式计算量的10倍。
数组p(n)表示矩阵的维数,上述数组可表示为
p(0)=30,p(1)=35,p(2)=15,p(3)=5,p(4)=10,p(5)=20,p(6)=25
数组m[i][j]表示最少乘的次数
s[i][j]表示m[i][j]最少乘次数时断开点k的值
输入参数{p0,p1,p2,p3....}存储于数组p中
特别注意m数组的构造顺序问题,不是一行一行的构造。
- public static void maxtrixChain(int []p,int [][]m,int [][]s) {
- int len = p.length - 1;
- for (int i = 1;i<=len;i++){
- m[i][i] = 0;//m数组的第一次构造(对角线)
- }
- //对m数组的赋值从对角线开始向顶点移动,越来越少
- for (int r = 2;r <= len;r++){//对m数组的第r次赋值及相应的顺序。
- for (int i =1;i <= len - r + 1;i++){
- int j = i + r - 1;
- m[i][j] =m[i][i]+ m[i+1][j] + p[i-1]*p[i]*p[j];
- s[i][j] = i;
- for (int k = i + 1;k < j;k++){
- int t =m[i][k]+ m[k+1][j] + p[i-1]*p[k]*p[j];
- if (t < m[i][j]){
- m[i][j] = t;
- s[i][j] = k;
- }
- }
- }
- }
- }

- public static void Traceback(int [][]s,int i,int j){
- //m[i][j]的最优断点s[i][j],将其分为m[i][ s[i][j] ]与 m[ s[i][j]+1 ][j]
- //按其规律递归,无限利用断点划分
- if (i==j) return;
- Traceback(s,i,s[i][j]);//m[i][ s[i][j] ]部分的递归
- Traceback(s,s[i][j]+1,j);//m[ s[i][j]+1 ][j]部分的递归
-
- System.out.println("A"+i+","+s[i][j]+"and A"+(s[i][j]+1)+","+j);
- }
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
输入: s = “leetcode”, wordDict = [“leet”, “code”] 输出: true 解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。
将大问题分解为规模小一点的子问题:
- 前 i 个字符的子串,能否分解成单词
- 剩余子串,是否为单个单词。
dp[i]
:长度为i
的s[0:i-1]
子串是否能拆分成单词。题目求:dp[s.length]
再利用j对s[0,i]进行分割,s[0,i]能否被划分为字典里存在的词取决于:
前缀部分s[0,j-1]的dp[j]是否为true
剩余部分s[j,i]是否能拆分为单词(原问题)
设序列X={},Y={
}的最长公共子序列Z={
},则
解释(1),若Xm=Yn,即表示序列X与序列Y的公共子序列的最后一项相同,即z的最后一项也是这个,因此求Xm与Yn的公共子序列Zk变成求
用c[i][j]来表示序列Xi与Yi的最长公共子序列的长度。
c[i-1][j-1]+1表示Xm=Yn=Zk
max{c[i][j-1],c[i-1][j]}表示Z是Xi-1与Yi的最长子序列或者Z是Xi与Yi-1的最长子序列
b[i][j]记录c[i][j]的值是由哪一个子问题的解得到的。
计算最优值(计算最优值的数组c[i][j])
public static int lcsLength(char []x,char []y,int[][]b){ int m = x.length-1; int n = y.length-1; int [][]c = new int[m+1][n+1]; for (int i=1;i<=m;i++) c[i][0]=0;//y序列为空 for (int i=1;i<=n;i++) c[0][i]=0;//x序列为空 for (int i=1;i<=m;i++){ for (int j=1;j<=n;j++){ if (x[i]==y[j]){//公共元素 c[i][j] = c[i-1][j-1]+1; b[i][j]=1;//子问题1 }else { if (c[i-1][j]>=c[i][j-1]){ c[i][j] = c[i-1][j]; b[i][j] = 2; }else { c[i][j] = c[i][j-1]; b[i][j] = 3; } } } } return c[m][n]; }
构造最长公共子序列(打印输出最长子序列)
public static void lcs(int i,int j,char []x,int [][]b){ if (i==0||j==0) return; if (b[i][j]==1){ lcs(i-1,j-1,x,b); System.out.println(x[i]);//此时当b[i][j]==1,第i项必为公共子序列的元素 } if (b[i][j]==2) lcs(i-1,j,x,b); if (b[i][j]==3) lcs(i,j-j,x,b); }
给定凸多边形P={V0,V1,V2,,,,Vn-1},以及定义在由多边形的边和弦组成的三角形上的权函数w。要求确定该凸多边形的三角剖分,使得该三角剖分所对应的权,即该三角剖分中诸三角形上权之和为最小。
权函数W w(Vi,Vj,Vk) = Vj*Vk+Vi*Vj+Vk*Vj
数组t[i][j]表示凸多边形{Vi-1,Vi,,,,Vj}的最优三角剖分对应的权函数值。即最优值。
设多边形{Vi-1,Vi}的权值为0
实现与矩阵连乘类似
数组t[i][j]表示凸多边形{Vi-1,Vi,,,,Vj}的最优三角剖分对应的权函数值。
s[i][j]表示t[i][j]对应的k的位置
t[i][j]的构建顺序
public static void minWeightTriangulation(int n,int[][]t,int [][]s){//t[][]表示 for (int i=1;i<=n;i++) t[i][i]=0; for (int r=2;r<=2;r++){//赋值是一个三角形状 for (int i=1;i<=n-r+1;i++){ int j = i+r-1; t[i][j] = t[i][i]+t[i+1][j]+w(i-1,i,j);//给一个初值便于比较大小 s[i][j] = i; for (int k=i+1;k<i+r-1;k++){ int u =t[i][k]+t[k+1][j]+w(i-1,k,j); if (u<t[i][j]){ t[i][j] = u; s[i][j] = k; } } } } }
- public static void mns(int []c,int[][]size) {
- int n=c.length-1;
- for(int j=0;j<c[1];j++)
- size[1][j]=0;
- for(int j=c[1];j<=n;j++) {
- size[1][j]=1;
- }
- for(int i=2;i<n;i++) {
- for(int j=0;j<c[i];j++) {
- size[i][j]=size[i-1][j];
- }
- for(int j=c[i];j<=n;j++) {
- size[i][j]=Math.max(size[i-1][j], size[i-1][c[i]-1]+1);
- }
- size[n][n]=Math.max(size[n-1][n], size[n-1][c[n]-1]+1);
-
- }
- }
-
- //构造最优解
- public static int traceback(int []c,int [][]size,int []net) {
- int n=c.length-1;
- int j=n;
- int m=0;
- for(int i=n;i>1;i--) {
- if(size[i][j]!=size[i-1][j]) {
- net[m++]=i;
- j=c[i]-1;
- }}
-
- if(j>=c[1])
- {
- net[m++]=1;
- }
-
-
- return m;
- }

- /*
- v[] 价值
- W[] 质量
- c 总质量
- m 存储m(i,j)的值
- */
- public static void knapack(int []v,int []w,int c,int [][]m){
- int n = v.length-1;//物品种类数
-
- // i=n
- int Wn = Math.min(w[n],c);//判断w(n)与c的大小
- for (int j=0;j<=c;j++){
- if (j<Wn) m[n][j]=0;
- if (j>=Wn) m[n][j]=v[n];
- }
- //i=n-1,n-2,,,,,1
- for (int i=n-1;i>=1;i--){
- int Wi = Math.min(w[i],c);
- for (int j=1;j<=c;j++){
- if (j>=Wi) m[i][j]=Math.max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
- if (j<Wi) m[i][j]=m[i+1][j];
-
- }
- }
- }

根据次可得,
若m( i , j ) = m( i+1 , j )可以得知i没有放入背包
若m( i , j ) = m( i + 1 , j - Wi ) + Vi可以得知将物品i放入了背包,此时背包容量为j-Wi
若m(n,j) = 0 得知物品n没有放入背包
若m(n,j) > 0 得知物品n放入背包
- //m[1][c]则是背包问题的最优值
- /*
- m 存储m(i,j)的值
- W[] 质量
- c 总质量
- x[i]判断物品i是否放入背包,1放入,0不放入
- */
- public static void traceback(int[][]m,int[]w,int c,int []x){
- int n = w.length-1;
- for (int i=1;i<n;i++){
- if (m[i][c]==m[i+1][c]) x[i]=0;//物品i没有放入背包
- else {//物品i放入背包
- x[i] = 1;
- c=c-w[i];
- }
- }
- //最后一个特殊讨论
- if (m[n][c]>0) x[n]=1;
- else x[n]=0;
- }

给定n种物品和一个背包。物品i的重量是wi,体积是bi,其价值是vi,背包的容量为c,容积为d。问:应该如何选择装入背包中的物品,使得装入背包中的物品的总价值最大?在选择装入背包额物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。
输入的第一行包含三个整数n, c ,d,分别表示物品的个数,背包能装重量和背包的容积。w[],v[],b[]分别表示各物品的重量、价值、体积。
与0-1问题类似,在此基础上再加上一个体积限制。
m[1][c][d]则是背包问题的最优值
x[i]判断物品i是否放入背包,1放入,0不放入
若m( i , j , k) = m( i+1 , j , k )可以得知i没有放入背包
若m( i , j , k) = m( i + 1 , j - Wi , k - Di) + Vi可以得知将物品i放入了背包,此时背包容量为j-Wi ,体积为k-Di
若m(n,j,k) = 0 得知物品n没有放入背包
若m(n,j,k) > 0 得知物品n放入背包
- //调用部分
- 输入 5 8 8
- v[]={2,1,4,3,5}
- w[]={1,4,2,3,5}
- b[]={2,3,5,2,4}
-
- public static void main(String[] args) {
- int c = 8;
- int d = 8;
- int []v = {0,2,1,4,3,5};//0用来占位,程序从下标1开始
- int []w = {0,1,4,2,3,5};
- int []b = {0,2,3,5,2,4};
- int [][][]m = new int[v.length][c+1][d+1];
- knapack(v,w,b,c,d,m);
- System.out.println("装入背包中物品总价值最大为:"+m[1][8][8]);
- int x[] = new int[v.length];
- traceback(m,w,b,c,d,x);
- System.out.print("装入的物品的序号为:");
- for (int i=0;i<v.length;i++){
- if (x[i]==1) System.out.print(i+" ");
- }
- }
-
-
-
- public static void knapack(int []v,int []w,int []b,int c,int d,int [][][]m){
- int n = v.length-1;
- int Wn = Math.min(w[n],c);
- int Dn = Math.min(b[n],d);
- for (int j=0;j<=c;j++){
- for (int k=0;k<=d;k++){
- if (j<Wn || k<Dn) m[n][j][k]=0;
- else m[n][j][k]=v[n];
- }
- }
- //i=n-1,n-2,,,,,1
- for (int i=n-1;i>=1;i--){
- int Wi = Math.min(w[i],c);
- int Di = Math.min(b[i],d);
- for (int j=1;j<=c;j++){
- for (int k=1;k<=d;k++){
- if (j>=Wi&&k>=Di) m[i][j][k]=Math.max(m[i+1][j][k],m[i+1][j-w[i]][k-b[i]]+v[i]);
- if (j<Wi||k<Di) m[i][j][k]=m[i+1][j][k];
- }
- }
- }
- }
-
-
-
- public static void traceback(int[][][]m,int[]w,int b[],int c,int d,int []x){
- int n = w.length-1;
- for (int i=1;i<n;i++){
- if (m[i][c][d]==m[i+1][c][d]) x[i]=0;//物品i没有放入背包
- else {//物品i放入背包
- x[i] = 1;
- c=c-w[i];
- d=d-b[i];
- }
- }
- //最后一个特殊讨论
- if (m[n][c][d]>0) x[n]=1;
- else x[n]=0;
- }

赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。