BZOJ 1044: [HAOI2008]木棍分割 DP,前缀和优化,二分答案
来源:互联网 发布:javascript图片轮播 编辑:程序博客网 时间:2024/04/30 23:15
Description
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连
接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长
度最大的一段长度最小. 并将结果mod 10007。。。
Input
输入文件第一行有2个数n,m.接下来n行每行一个正整数Li,表示第i根木棍的长度.n<=50000,0<=m<=min(n-1,10
00),1<=Li<=1000.
Output
输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.
Sample Input
3 2
1
1
10
Sample Output
10 2
HINT
两种砍的方法: (1)(1)(10)和(1 1)(10)
解题方法:好题好题好题,磨了一晚上加一早上终于过去了这个题,好难。我们来说一说这个题的思路,第一问显然一个二分就可以了。关键是第二问,我们容易想到这样一个DP,我们设f[i][j]表示前i段一共分割了j次,设ss[i]为a[i]的前缀和,然后写出dp方程:
f[i][j] = Σf[k][j-1] 其中k要满足的条件是(1 <= k < i) && (ss[i] - ss[k] <= len)(这是很容易从题目中得出的)。
于是我们就可以完成了。
但是这样也太简单了吧……毕竟是HAOI的题目,如果这么简单就是NOIP难度了(虽然本人不否认以前的省选题目也有NOIP难度的)
然后注意到数据范围:n<=50000,0<=m<=min(n-1,1000)
我们注意到我们程序的时间复杂度实际上是O(n^2 m) 的,这明显就是爆了时间的。
那然后该怎么办呢?
我们可以注意到,如果我们设sumf 表示枚举到k的时候Σf[k][j-1],(1 <= k < i) && (ss[i] - ss[k] <= len),mink表示满足(1 <= k < i) && (ss[i] - ss[k] <= len)的最小的k。
其实对于 f[i][Now] ,其实是 f[mink][Last]…f[i-1][Last] 这一段 f[k][Last] 的和,mink 是满足 Sum[i] - Sum[k] <= Len 的最小的 k ,对于从 1 到 n 枚举的 i ,相对应的 mink 也一定是非递减的(因为 Sum[i] 是递增的)。我们记录下 f[1][Last]…f[i-1][Last] 的和 Sumf ,mink 初始设为 1,每次对于 i 将 mink 向后推移,推移的同时将被舍弃的 p 对应的 f[p][Last] 从 Sumf 中减去。那么 f[i][Now] 就是 Sumf 的值。(此段复制自Evensgn的博客,因为我觉得自己可能写不出来这么详细)
这样我们就不必枚举k,时间复杂度就降低到可以接受的O(nm)了。
但是这样就完成了?别天真了,还有一个坑那,时间解决了,空间呢?我们的空间复杂度是O(nm)啊,用计算器算一下明显超了。
这时候的DP有一个技巧(类似于飞扬的小鸟NOIP2014),我们发现其实j所属的那一维,只能由j-1转移而来,所以可以使用最常用的手段——滚动数组,来滚动掉第二维
使用now和pre,f[maxn][2],now和pre只能为0或1,且pre = now^1,每完成一遍外层m循环更新now ^= 1,pre = now^1。
这样子我们的空间复杂度也降到可以接受的O(n)辣了!(上面的题解是粘贴的这个博客的,见这里) 我和他的思路是一样的。
代码如下:
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 50005;const int P = 10007;int a[N], sum[N], pre[N];int dp[N][2];int presum[N];int ans, n, m;int answer;bool check(int x){ int num = 0, cnt = 0; for(int i = 1; i <= n; i++){ if(num + a[i] <= x){ num += a[i]; } else{ if(a[i] > x) return false; else{ cnt++; num = a[i]; } } } if(cnt <= m) return true; else return false;}int main(){ scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); sum[i] = sum[i-1] + a[i]; } int l = 1, r = 1e9; while(l < r){ int mid = (l + r) / 2; if(check(mid)) r = mid; else l = mid + 1; } ans = l; int now = 0; int sumf = 0; int mink = 0; for(int i = 0; i <= m; i++){ sumf = 0; mink = 1; for(int j = 1; j <= n; j++){ if(i == 0){ if(sum[j] <= ans) dp[j][now] = 1; else dp[j][now] = 0; } else{ while(mink < j && sum[j] - sum[mink] > ans){ sumf -= dp[mink][now^1]; sumf = (sumf + P) % P; mink++; } dp[j][now] = sumf; } sumf += dp[j][now^1]; sumf = (sumf + P) % P; } answer += dp[n][now]; answer = (answer + P) % P; now ^= 1; } printf("%d %d\n", ans, answer); return 0;}
- 1044: [HAOI2008]木棍分割 二分答案+DP+前缀和优化
- BZOJ 1044: [HAOI2008]木棍分割 DP,前缀和优化,二分答案
- BZOJ 1044: [HAOI2008]木棍分割(二分答案+DP)
- BZOJ 1044: [HAOI2008]木棍分割 二分 dp
- HAOI2008 木棍分割 二分答案 前缀和优化 单调队列 滚动数组
- BZOJ 1044 HAOI2008 木棍分割 二分答案+动态规划
- BZOJ 1044: [HAOI2008]木棍分割|动态规划|二分答案
- bzoj 1044: [HAOI2008]木棍分割 二分答案+动态规划
- [BZOJ1044]HAOI2008木棍分割|DP|二分答案
- BZOJ1044 [HAOI2008]木棍分割(二分答案/单调性优化dp+递推优化)
- NKOJ 4244 (HAOI 2008) 木棍分割 (二分答案+DP+单调队列+前缀和优化+滚动数组)
- BZOJ 1044: [HAOI2008]木棍分割
- bzoj.1044: [HAOI2008]木棍分割
- 【BZOJ 1044】[HAOI2008]木棍分割
- BZOJ 1044 [HAOI2008]木棍分割 二分+动态规划
- 【BZOJ1044】【tyvj3511】【codevs1870】木棍分割,二分答案+滚动数组+前缀和DP
- BZOJ1044 [HAOI2008]木棍分割 【二分+Dp】
- bzoj 1044 [HAOI2008]木棍分割 题解
- 手动配置easyx头文件和库文件到vs2010中
- c 一些宏的使用
- ZCMU-1808-FJ的字符串
- [Usaco2006 Oct] Building the Moat护城河的挖掘
- Android开发实践:拷贝assets到sdcard
- BZOJ 1044: [HAOI2008]木棍分割 DP,前缀和优化,二分答案
- 读《黑客与画家》感想(2)
- linux编译busybox,uboot,kernel等常用到的命令及出现过的问题
- 韩信点兵
- ZCMU-1841-完美的代价
- [SDOI2012] 最近最远点对
- 使用Retrofit和Rxjava下载启动图图片
- Tomcat正常启动,页面报HTTP Status 500 - Class com.fjc.Hello is not a Servlet,控制台报 cannot be cast to javax.se
- 观察者模式高级应用--自定义Swing事件监听器