附上这道题的链接:http://poj.org/problem?id=1769
题目的意思是有一个装置可以输出n个数的最大值, 这个装置由m个排序器组成, 每个排序器可以将这n个数从s 到 t的数按照从小到大的顺序排列, 有一个人发现将m个排序器中的一些排序器去掉仍然不影响功能, 现在问你最少需要多少个排序器可以完成功能。 我们可以定义dp[i][j]为前i个排序器将第1个数提到j所需要的最少的排序器的数量, 那么当t[i] == j的时候 dp[i][j] = min(dp[i-1][j] , min(dp[i-1][si -- ti]) + 1) 当ti != j的时候 dp[i][j] = dp[i-1][j], 我们观察状态数有m*n个, 显然直接求解会超时, 因此我们考虑使用线段树来优化 见挑战程序设计 p207 , 代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 500000 + 100; const int inf = 0x3f3f3f3f; int n, m; struct segment{ int l, r; int x; }seg[3*maxn]; void build(int rt, int l, int r) { seg[rt].l = l; seg[rt].r = r; if(l == r) { int value = inf; if(l == 1) value = 0; seg[rt].x = value; return ; } int chl = 2*rt, chr = 2*rt + 1; int mid = (l + r) / 2; build(chl, l, mid); build(chr, mid+1, r); seg[rt].x = min(seg[chl].x, seg[chr].x); } int query(int rt, int l, int r) { if(seg[rt].l == l && seg[rt].r == r) { return seg[rt].x; } int mid = (seg[rt].l + seg[rt].r)/2; if(r <= mid) return query(2*rt, l, r); else if(l > mid) return query(2*rt+1, l, r); else{ int v1 = query(2*rt, l, mid); int v2 = query(2*rt+1, mid+1, r); return min(v1, v2); } } void update(int rt, int i, int c) { if(seg[rt].l==seg[rt].r && seg[rt].l == i) { seg[rt].x = c; return ; } int mid = (seg[rt].l + seg[rt].r)/2; if(i <= mid) update(2*rt, i, c); else update(2*rt+1, i, c); seg[rt].x = min(seg[2*rt].x, seg[2*rt+1].x); } int main() { while(scanf("%d%d", &n, &m) != EOF) { build(1, 1, n); for(int i=0; i<m; i++) { int s, t; scanf("%d%d", &s, &t); int v1 = query(1, s, t) + 1; int v2 = query(1, t, t); int v3 = min(v1, v2); update(1, t, v3); } printf("%d\n", query(1, n, n)); } return 0; }
时间: 2024-11-09 00:50:49