POJ2373 - 单调队列

这题的细节好多。。。。

首先,因为一个花丛区间是不能同时被两个木板覆盖的,所以,我们要把那些有交集的花丛区间都合并到一个花丛区间,这是第一步。

从这里开始,接下来,我们讨论中出现的“花丛区间”,都是指“合并以后的花丛区间“,也就是说,花丛区间之间是没有交集的,而且将它们按照左端点排序。设花丛区间数组为ranges[1...N]。

然后,我们把所有的合法点都标记出来,所谓的合法点x,是指:
如果存在某个花丛区间,使得x处于该花丛区间的中间区域(不包括两个端点),那么x是非法点。否则,x是合法点。

我们设f(i) 表示 用若干块木板合法地覆盖区间[0.....i]需要的最小的木板数目,而且i是合法点。所谓合法地覆盖,也就是说,每个花丛区间都被且只被一块木板覆盖。
看一下转移方程:
f(i) = min{ f(k) } + 1,  其中i - 2*b <= k <= i - 2*a     (#)
这个方程的含义是,为了得到f(i),我们要先算出前面的若干个f(k),且i - 2*b <= k <= i - 2*a。
而边界条件应该是f(0) = 0

由转移方程的形式,我们可以看出,这题可以用单调队列优化。
要注意的是,在方程(#)里,i和k,都肯定是偶数。


#include <algorithm>
#include <vector>
#include <deque>
#include <cstdio>
#include <cassert>
using namespace std;


#ifdef DEBUG
#define trace printf
#endif

template <class T1, class T2>
struct Tuple {
  T1 _1;
  T2 _2;
};

template <class T1, class T2>
Tuple<T1, T2> makeTuple(T1 t1, T2 t2) {  Tuple<T1, T2> r;  r._1 = t1;  r._2 = t2;  return r; }

typedef Tuple<int, int > TII;

bool lessTuple(const TII & a, const TII & b) {
	return a._1 < b._1;
}


const char TRUE = '\1';
const char FALSE = '\0';

char isValid[1000001];
deque<Tuple<int, int > > dQ;
deque<Tuple<int, int > > Q;
vector<Tuple<int, int> > ranges;
vector<Tuple<int, int> > combinedRanges;

int L, N, A, B;

void input() {
  scanf("%d %d", &N, &L);
  scanf("%d %d", &A, &B);
  int i;
  int S, E;
  ranges.clear();
  ranges.reserve(N);
  for (i = 0; i < N; ++i) {
    scanf("%d %d", &S, &E);
    ranges.push_back(makeTuple(S, E));
		// printf("@@ %d %d\n", ranges[ranges.size() - 1]._1, ranges[ranges.size() - 1]._2);
  }
}

void combine(const vector<Tuple<int, int > > & a, int len, vector<Tuple<int, int > > & res) {
  int i = 0;
  int j;

  while(1) {
    int start = a[i]._1;
    int end = a[i]._2;
    for (j = i + 1; j < len; ++j) {
      if (end <= a[j]._1 || a[j]._2 <= start) break;
      start = min(start, a[j]._1);
      end = max(end, a[j]._2);
    }
    res.push_back(makeTuple(start, end));
		// printf("*** %d %d\n", start, end);
    i = j;
    if (i >= len) break;
  }
}




void initValidLocations(vector<Tuple<int, int > > & a) {
  int i, j, k;
  for (i = 0; i <= 1000000; ++i) isValid[i] = TRUE;
  // assert(0);
  for (j = 0; j < a.size(); ++j) {
    // printf("@ %d %d\n", a[j]._1, a[j]._2);
    for (k = a[j]._1 + 1; k < a[j]._2; ++k) isValid[k] = FALSE;
  }
}


void deleteTheOld(int i) {
  while (!dQ.empty() && dQ.front()._1 < i) dQ.pop_front();
}

void insertTo(Tuple<int, int> x) {
  while(!dQ.empty() && dQ.back()._2 >= x._2) dQ.pop_back();
  dQ.push_back(x);
}



void dp() {
  int i;
  int a = A, b = B;
  int fi;

	bool existSolution = false;

	Q.push_back(makeTuple(0, 0));
  for (i = 2; i <= L; i+=2) {
    if (!isValid[i]) continue;

		while(!Q.empty() && Q.front()._1 <= i - 2 * a) {
			insertTo(Q.front());
			Q.pop_front();
		}

		deleteTheOld(i - 2 * b);

		if (!dQ.empty()) {
			fi = dQ.front()._2 + 1;
			Q.push_back(makeTuple(i, fi));
			if (i == L) existSolution = true;
		}
  }

  printf("%d\n", existSolution ? fi : -1);

}


int main() {
  input();
	sort(ranges.begin(), ranges.end(), lessTuple);
  combine(ranges, N, combinedRanges);
  initValidLocations(combinedRanges);
  // assert(0);
  dp();
  return 0;
}


你可能感兴趣的:(POJ2373 - 单调队列)