校OJ 最大m段乘积和最小m段和
来源:互联网 发布:淘宝女士运动套装 编辑:程序博客网 时间:2024/06/14 08:51
描述:给一个n位的数和m,代表将n位数分成m段,求最大m段乘积和最小m段和,同样也可以求m段的最小积和最大和,原理一样
刚开始的时候并没有做得出来,因为dp的状态转移方程写错了,还是太嫩了,没有理解好思路,弄了半天原来其实状态方程很好理解,但是用到了n^3,所以没太敢去想太大的复杂度。。。
设dp(i,j)=max(dp(i-1,k)+sum[k+1,j]),其中i表示第i段,j表示已j结尾之前的字符,其实就是寻找前i-1段+在i-1断开之后到j之积最大或和最小,其实还是很好理解的,
思路:
设w(h,t)是S从h位开始的共t位数字组成的十进制数,约定从1开始从左向右对位进行编号,
即最左一位定为第1位。
f(i,j)表示S的前i位数划分成j段后的最大j段乘积。
t(i,j)表示S的前i位数划分成j段后的最小j段和。
无论是f(i,j)还是t(i,j),这里只考虑i>=j的情况,因为每个段至少1位,因此i必然大于等于j。
显然f(i,j)和t(i,j)都具有最优子结构。
一、最大m段乘积的动态规划递归式
边界: 当j=1时,f(i,1) = w(1,i), 1<=i<=n 表示当只划分1个段时,最大段乘积
就是从第1位开始共i位数(i>=1)的十进制数值。
当j>=2时,计算f(i,j)的动态规划的递归式如下:
f(i,j) = max{ f(k, j-1) * w(k+1, i-k) | k from j-1 to i-1 } j>=2 && j<=i<=n
(即让k从j-1到i-1变化,找f(k, j-1)和w(k+1, i-k)乘积的最大值)
这个公式这样理解:
当 j>=2 && j<=i<=n 时,现在要求解的问题是前i位划分为j个段的最大j段乘积,
这时考虑前k位,划分j-1个段(因为最后一个段至少占1位,而前j-1个段又至少有j-1位,所以 j-1 <= k <= i-1),
先获得这j-1个段的最大段乘积,再乘以从第k+1位到第i位(共i-k位,这是最后一个段,即第j个段)的十进制数,
让k从j-1到i-1循环,求f(k, j-1)和w(k+1, i-k)乘积的最大值。
二、最小m段和的动态规划递归式
最小m段和公式的分析和最大m段乘积的公式分析是相同的。
边界: 当j=1时,t(i,1) = w(1,i), 1<=i<=n 表示当只划分1个段时,最小段和就是从第1位开始共i位
数(i>=1)的十进制数值。
当j>=2时,计算t(i,j)的动态规划的递归式如下:
t(i,j) = min{ t(k, j-1) + w(k+1, i-k) | k from j-1 to i-1 } j>=2 && j<=i<=n
(即让k从j-1到i-1变化,找t(k, j-1)和w(k+1, i-k)之和的最小值)
OJ上还需递归回去寻找切割的位置,所以处理起来有点麻烦,但我不太会搜索和递归,所以开多一个数组保存每次切割形成的数,在从后往前找就行了
由于OJ上已经关闭,不能提交了,所以不知有没错,不过在nyoj上提交可以过,但是是不需递归回去的,唉,每次都有资源的时候不好好珍惜,错过了又后悔,但死性不改,到现在还是什么都不懂,弱鸡一个,和别人的差距越来越凸显了,唉,好恨自己。
#include <iostream>using namespace std;#define N 20#define inf 9999999int dp[N][N],fp[N][N];int a[N],b1[N],b2[N],m,n;char s[N];int multiply(){ for(int i=2;i<=n;i++) { dp[1][i]=dp[1][i-1]*10+a[i]; dp[i][i]=dp[i-1][i-1]*a[i]; fp[1][i]=dp[1][i];// fp[i][i]=a[i];// } for(int i=2;i<=m;i++) { for(int j=i+1;j<=n;j++) { dp[i][j]=0; for(int k=i-1;k<j;k++) { int ps=0; for(int p=k+1;p<=j;p++) ps=ps*10+a[p]; if(dp[i][j]<dp[i-1][k]*ps) { dp[i][j]=dp[i-1][k]*ps; fp[i][j]=ps; } } } } /*for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) cout<<dp[i][j]<<"("<<fp[i][j]<<")"<<" "; cout<<endl; }*/ return dp[m][n];}int sum(){ for(int i=2;i<=n;i++) { dp[1][i]=dp[1][i-1]*10+a[i]; dp[i][i]=dp[i-1][i-1]+a[i]; } for(int i=2;i<=m;i++) { for(int j=i+1;j<=n;j++) { dp[i][j]=inf; for(int k=i-1;k<j;k++) { int ps=0; for(int p=k+1;p<=j;p++) ps=ps*10+a[p]; dp[i][j]=min(dp[i-1][k]+ps,dp[i][j]); } } } /*for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) cout<<dp[i][j]<<" "; cout<<endl; }*/ return dp[m][n];}void dfs_multiply(){ b1[m]=fp[m][n]; int t=dp[m][n]/fp[m][n]; for(int i=m-1;i>=1;i--) { for(int j=i;j<=n;j++) { if(dp[i][j]==t) { b1[i]=fp[i][j]; t=dp[i][j]/fp[i][j]; break; } } }}void dfs_sum(){ b2[m]=fp[m][n]; int t=dp[m][n]-fp[m][n]; for(int i=m-1;i>=1;i--) { for(int j=i;j<=n;j++) { if(dp[i][j]==t) { b2[i]=fp[i][j]; t=dp[i][j]-fp[i][j]; break; } } }}int main(){ cin>>n>>m>>s; for(int i=0;i<n;i++) a[i+1]=s[i]-'0'; fp[1][1]=dp[1][1]=a[1]; int p1=multiply(); dfs_multiply(); int p2=sum(); dfs_sum(); cout<<p1<<" "<<p2<<endl; for(int i=1;i<=m;i++) { if(i==1) cout<<b1[i]; else cout<<"*"<<b1[i]; } cout<<"="<<p1<<endl; for(int i=1;i<=m;i++) { if(i==1) cout<<b2[i]; else cout<<"+"<<b2[i]; } cout<<"="<<p2<<endl; return 0;}
- 校OJ 最大m段乘积和最小m段和
- 11090最大m段乘积和最小m段和-动态规划
- 最小m段和
- 最小m段和
- 最小m段和
- 最小m段和
- 校OJ 17089 最大m子段和
- 最小m段和问题
- 最小m段和问题
- 最小m段和(DP)
- 最小M段和问题
- 最小m段和问题
- 最小m段和问题
- 最小m段和问题
- 最小m段和问题
- 最小m段和问题
- 最小m段和问题
- 最小m段和问题
- hdu2577
- UIScrollView 基本用法
- 彻底 卸载/删除 jdk java
- Flask源码阅读(七)——上下文变量
- [Chromium]如何安全的使用PostTask
- 校OJ 最大m段乘积和最小m段和
- C/C++练习题解析(一)
- 避免无休止的技术辩论,需要一套有效的决策流程
- Unity3D将来时:IL2CPP(上)
- js实现购物车效果
- 简易的Java树节点
- 随记,页面文字过长默认以...表示
- [看书日记20160108]ipc机制,imageLoader的实现
- Codeforces 615D Multipliers 【组合数学】