codeforces 487B Strip dp

传送门:cf 487B

给定一个长度为n的数组,要求把数组分为若干部分满足下面两个条件

(1):每个部分至少含有l个元素

(2):每个部分中两两数的差值的最大值不超过s

问在满足上述两个条件的情况下,最少能分成多少个部分。

可以预处理出每个点最靠左的可行起点位置left,然后dp处理出结果,状态转移方程如下:

ans[i] = min(f[k]) + 1
   left[i]<=k<=i-l     当k无可行解时用INF表示无解

预处理的过程与求ans的过程都需要在整个过程中记录一个区间的值,并且需要快速找出这个区间的最大以及最小值,可以用multiset来解决

multiset 与set 类似可以有序存储数据,且multiset支持重复的数据

/******************************************************
 * File Name:   b.cpp
 * Author:      kojimai
 * Create Time: 2014年11月22日 星期六 21时39分25秒
******************************************************/

#include<cstdio>
#include<set>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
#define FFF 100005
int a[FFF],ans[FFF],Left[FFF];
multiset<int> p;
int main()
{
	int n,l,s;
	cin>>n>>s>>l;
	for(int i = 0;i < n;i++)
		cin>>a[i];
	int now = 0;
	for(int i = 0;i < n;i++) {
		while(!p.empty()) {
			int Min = *p.begin(),Max = *(--p.end());
			if(Max - a[i] <= s && a[i] - Min <= s) break;
			p.erase(p.lower_bound(a[now]));//当前情况不满足,因此去除最左边的端点再判断一次,知道满足条件或者multiset为空
			now++;
		}
		Left[i] = now;//Left存每个点最左边的连续串起点
		p.insert(a[i]);
	}
	now = 0;
	p.clear();
	ans[0] = 0;
	for(int i = 1;i <= n;i++) {
		if(i >= l)
			p.insert(ans[i-l]);
		while(now <= i-l && now < Left[i-1]) {
			p.erase(p.lower_bound(ans[now]));
			now++;
		}
		if(p.empty())
			ans[i] = FFF;
		else
			ans[i] = *p.begin() + 1; //最小值排在最前面
	}
	if(ans[n] >= FFF)
		cout<<-1<<endl;
	else
		cout<<ans[n]<<endl;
	return 0;
}
时间: 2024-09-28 14:03:42

codeforces 487B Strip dp的相关文章

Codeforces 487B. Strip DP+线段树+二分

dp[ i ]表示到第i个位置最少要分多少下, dp[ i ] = min ( dp [ i ] , dp [ j ] + 1 ) j 在合适的范围内 (  满足长度和最值差 ) 对整个数组建立线段树维护最大值和最小值这样就可在nlogn的时间里求出某一段的最值差,这个范围是满足单调性的,所以对于每个i可以二分出j的最小值 . 对每个dp[i]建立线段树,可以在nlogn时间内求出最小的j. 所以总时间复杂度n^2logn B. Strip time limit per test 1 secon

Codeforces 487B. Strip(求区间最值+线段树上的dp)

B. Strip time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Alexandra has a paper strip with n numbers on it. Let's call them ai from left to right. Now Alexandra wants to split it into some p

CodeForces 487B Strip

题意: n(10^5)个人分组  每组最少L个人  每组的差异为组中人最大价值-最小价值  要求差异均不超过S  问最少分几组 思路: 假设已经知道组的区间[l,r]那么计算差异就是简单的rmq问题  可以用线段树搞 我们可以用dp[i]表示到i位置产生的最少组数 假设从i位置开始分一组  会影响到哪些dp呢  我们可以利用二分+rmq找到这个组最远延伸到哪里  从L到最远点这个区间的dp就是受这一组影响的  那么对于一段连续的区间的值的更新  我们也可以用线段树搞 那么总复杂度就为O(n(lo

codeforces 487B B. Strip(rmq+线段树+二分)

题目链接: codeforces 487B 题目大意: 给出一个序列,要把序列划分成段,每一段最少有L个元素,段中的最大元素和最小元素之差不大于s,问划分的段的最少的数量是多少. 题目分析: 首先用rmq维护区间最大值和区间最小值. 然后按顺序扫描数组,线段树维护的数组,每个记录当前点作为最后一个点的前i个点划分的最小的段数,那么每次更新就是二分找到可以转移到我的最远距离,然后再选取与我距离大于l的那部分,取最小值即可. 最终结果就是线段树维护的数组的最后一个位置的元素的值. AC代码: #in

Codeforces 13C Sequence --DP+离散化

题意:给出一个 n (1 <= n <= 5000)个数的序列 .每个操作可以把 n 个数中的某一个加1 或 减 1.问使这个序列变成非递减的操作数最少是多少 解法:定义dp[i][j]为将前i个数变为以j为结尾的非递减序列的最少操作次数. 则有: dp[i][j] = min(dp[i][j], min(dp[i][k]) + Cost(原来第i个位置上的数转换到j))  (1 <= k <= j) 即前i个数以j结尾的状态可以由前i-1个数以小于等于j的k结尾的状态转移过来,取

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

Codeforces 57C Array dp暴力找规律

题目链接:点击打开链接 先是计算非递增的方案, 若非递增的方案数为x, 则非递减的方案数也是x 答案就是 2*x - n 只需求得x即可. 可以先写个n3的dp,然后发现规律是 C(n-1, 2*n-1) 然后套个逆元即可. #include<iostream> #include<cstdio> #include<vector> #include<string.h> using namespace std; #define ll long long #def

Codeforces 413D 2048(dp)

题目连接:Codeforces 413D 2048 题目大意:2048的游戏,两个相同的数x可以变成一个2*x,先给出n,表示在一个1*n的矩阵上面玩2048,规定每次向左移动,并且每次出现一个,给出序列n,表示出现的块的值,0表示既可以是2也可以是4,问说有多少种可能,使得游戏结束后的最大块的值大于等于2^k. 解题思路:dp[i][j][x]表示第i个位置,值为j,x表示先前有没有出现过大于2^k的数: 这种递增的情况可以直接表示为14(总和,以为后面的2,4如果变大,就肯定能和8想合在一起

Codeforces 455A Boredom (dp)

很裸的dp 状态转移方程 dp[i]=max(dp[i-1],dp[i-2]+dp[i]*i) #include<bits/stdc++.h> using namespace std; long long dp[100020]; int main() { int n,a; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a); dp[a]++; } for(int i=2;i&