poj 1505 dp(数列分段,最大段和最小)
2015-04-07 16:45
225 查看
题意:给出一个有n个数的数列,要求把这n个数分成m段,不能改变原数列的顺序。每段至少一个数。求使得加和最大的那段的和最小的划分方案。如果有多组解的话先要保证第一段和尽量小,若仍有多组解,要先保证第二段和尽量小,以此类推。(与poj3273题意相同,只是需要输出答案)
思路:dp。dp[i][j]表示将前j个数分成i段的段和最大的最小值。一开始dp过程中记录路径然后输出,结果wa。分析原因在于输出不符合题意,比如数据:
5 3
1 1 1 1 10
如果利用dp记录的话输出为1 1 / 1 1 / 10,而正确的输出应该为:1 / 1 1 1 / 10。原因在于前四个数分成两段的最优值不是整个输出的最优值。注意到最优值的结果是不变的(dp[m]
),所以拿最优值的结果贪心输出最优解即可,贪心的过程就是从后向前,每次取不超过dp[m]
值的最大的组合(注意剩出足够的保证每段至少有一个数)。
还有其他做法二分+贪心输出(与前述输出方法一样)。二分查找这个加和最大的段的加和最小值。在查找过程中,每次枚举这个加和,对数列从左到右看一遍,看在每段加和不超过这个枚举值的前提下最少可以将这个数列分成几段。如果分段数小于等于m则这个枚举值偏大或者刚刚好,如果大于m则说明枚举值偏小。
采用3273二分的思路:
思路:dp。dp[i][j]表示将前j个数分成i段的段和最大的最小值。一开始dp过程中记录路径然后输出,结果wa。分析原因在于输出不符合题意,比如数据:
5 3
1 1 1 1 10
如果利用dp记录的话输出为1 1 / 1 1 / 10,而正确的输出应该为:1 / 1 1 1 / 10。原因在于前四个数分成两段的最优值不是整个输出的最优值。注意到最优值的结果是不变的(dp[m]
),所以拿最优值的结果贪心输出最优解即可,贪心的过程就是从后向前,每次取不超过dp[m]
值的最大的组合(注意剩出足够的保证每段至少有一个数)。
还有其他做法二分+贪心输出(与前述输出方法一样)。二分查找这个加和最大的段的加和最小值。在查找过程中,每次枚举这个加和,对数列从左到右看一遍,看在每段加和不超过这个枚举值的前提下最少可以将这个数列分成几段。如果分段数小于等于m则这个枚举值偏大或者刚刚好,如果大于m则说明枚举值偏小。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 505 int dp ,s ; int T,n,m; void print(int m,int n,int now,int res){//m表示是当前 if(!n) return; if(now+s > res || m>n){//如果段长超过最优值或者剩下的不够每段至少一个数,那么此段输出完毕。 print(m-1,n,0,res); printf("/ "); }else{ print(m,n-1,now+s ,res); printf("%d ",s ); } } int main(){ scanf("%d",&T); while(T--){ int i,j,k,res,now,a; scanf("%d %d",&n,&m); dp[1][0] = 0; for(i = 1;i<=n;i++){ scanf("%d",&s[i]); dp[1][i] = dp[1][i-1]+s[i]; } for(i = 2;i<=m;i++) for(j = i;j<=n;j++){ for(k = j,now=0,a=0x3fffffff;k>=i;k--){ now += s[k]; if(max(now,dp[i-1][k-1]) <= a) a = max(now,dp[i-1][k-1]); else break; } dp[i][j] = a; } print(m,n,0,dp[m] ); putchar('\n'); } return 0; }
采用3273二分的思路:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 505 int dp ,s ; int T,n,m; void print(int m,int n,int now,int res){//m表示是当前 if(!n) return; if(now+s > res || m>n){//如果段长超过最优值或者剩下的不够每段至少一个数,那么此段输出完毕。 print(m-1,n,0,res); printf("/ "); }else{ print(m,n-1,now+s ,res); printf("%d ",s ); } } int main(){ scanf("%d",&T); while(T--){ int i,j,k,low,high,mid,num,sum; scanf("%d %d",&n,&m); low = high = 0; for(i = 1;i<=n;i++){ scanf("%d",&s[i]); low = max(low,s[i]);//关键点之一,low如果从0开始则会出现错误 high += s[i]; } while(low <= high){ mid = (low+high)>>1; num = sum = 0; for(i = 1;i<=n;i++){ sum += s[i]; if(sum > mid){ num ++; sum = s[i]; } } num++;//加上最后一天的组 if(num > m) low = mid+1; else high = mid-1; } print(m,n,0,low); putchar('\n'); } return 0; }
相关文章推荐
- poj 2479 dp求分段最大和
- POJ 1976 A Mini Locomotive (DP 最大连续m子段和)
- POJ 1050 To the Max && POJ 2479 Maximum sum(DP最大连续子段和)
- POJ 1976 A Mini Locomotive (DP 最大连续m子段和)
- poj 2263 Heavy Cargo 求a到b路上最小值中的最大值
- POJ 3615 牛的最小最大起跳高度问题 Floyd算法的变形应用
- RMQ 问题 POJ 3264 求解任意指定区间内的最小值和最大值
- POJ 3308最大流最小割
- POJ 3615 牛的最小最大起跳高度问题 Floyd算法的变形应用
- 【DP生成回文字符串的最小插入字符数】POJ 1159
- 简单的dp@poj(2)2479最大子段和
- poj 1050 【最大子矩阵和DP】
- 一个最小 一个最大 poj 1379 hdu 3932
- poj 2594 Treasure Exploration 有向无环图最小路径覆盖变形(每个点能走多次)+FLOYD = N-最大匹配
- poj 1422 Air Raid 有向无环图最小路径覆盖=N-最大匹配
- poj 1887 Testing the CATCHER dp 最大降序
- POJ 1050 最大子矩阵和 DP
- poj 3041 Asteroids 二分图的最大匹配==最小覆盖点。
- POJ 2479 + POJ 2593(DP 最大双子序列 ^_^)
- swjtu1583 用DP或最小费用最大流求一点至另一点两条路径的最大价值,同一格点算一次