解题思路:线段树优化DP。如果n很小,那么这题有好多解法,用最短路(如果两个区间相连就添加一条边)或者朴素的N^2的DP(状态转移方程dp[endi] = min(dp[k]) + ti ,begi<=k<=endi)。这题n很大但仍然考虑用DP,因为每次状态转移都要用到前面某个区间的最小值,而这个查询操作用线段树就能以logn的复杂度完成,还有更新操作,只要更新endi这个点就可以,这就是最简单的单点更新线段树。为什么更新的时候只要更新一点就可以呢?为了达到这种效果我们按照区间的endi对所有区间排序,这样当前的endi肯定比之前的大,查询的时候是查询整个区间,那么endi那个点所在的最小权值就能反应整个区间的情况。
3
3 10
5 1 3
8 2 5
10 9 2
4 5
2 1 1
3 2 1
4 3 1
8 4 1
5 9
5 1 1
10 4 10
8 1 10
11 6 1
7 3 8
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define MAX 210000 #define int64 long long #define INF (0xffffffffffff) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct node { int beg,end,time; }arr[MAX]; int64 ans,Min[MAX*3]; int n,m,tot,cnt,dis[MAX*3]; inline int64 min(int64 a,int64 b) { return a<b?a:b; } int Binary(int x) { int low = 1,high = tot,mid; while (low <= high) { mid = (low + high) >> 1; if (dis[mid] == x) return mid; else if (dis[mid] < x) low = mid + 1; else high = mid - 1; } } void DisCret() { //离散化 int i; dis[cnt++] = 1,dis[cnt++] = m; sort(dis+1,dis+cnt); for (i = 2; i < cnt; ++i) if (dis[i] != dis[i-1]) dis[++tot] = dis[i]; for (i = 1; i <= n; ++i) { arr[i].beg = Binary(arr[i].beg); arr[i].end = Binary(arr[i].end); } } int cmp(node a,node b){ if (a.end != b.end) return a.end < b.end; else return a.beg < b.beg; } void Push_Up(int rt) { Min[rt] = Min[rt<<1]<Min[rt<<1|1]?Min[rt<<1]:Min[rt<<1|1]; } void Build_Tree(int l,int r,int rt) { if (l == r) { Min[rt] = l == 1 ? 0 : INF; return; } int m = (l + r) >> 1; Build_Tree(lson); Build_Tree(rson); Push_Up(rt); } int64 Query_Tree(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) return Min[rt]; int m = (l + r) >> 1; int64 temp = INF; if (m >= L) temp = min(temp,Query_Tree(L,R,lson)); if (m + 1 <= R) temp = min(temp,Query_Tree(L,R,rson)); return temp; } void Update(int L,int64 x,int l,int r,int rt) { if (l == r) { Min[rt] = min(Min[rt],x); return; } int m = (l + r) >> 1; if (L <= m) Update(L,x,lson); else Update(L,x,rson); Push_Up(rt); } int main() { int i,j,k,t,cas = 0; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); cnt = tot = 1; for (i = 1; i <= n; ++i) { scanf("%d%d%d",&arr[i].end,&arr[i].beg,&arr[i].time); dis[cnt++] = arr[i].beg,dis[cnt++] = arr[i].end; } DisCret(); //离散化 Build_Tree(1,tot,1); //构建线段树 sort(arr+1,arr+1+n,cmp); //排序后才没有后效性 for (i = 1; i <= n; ++i) { int64 tp = Query_Tree(arr[i].beg,arr[i].end,1,tot,1); if (tp == INF) continue; Update(arr[i].end,tp+arr[i].time,1,tot,1); //dp[arr[i].end] = min(dp[k]) + arr[i].time; } ans = Query_Tree(Binary(m),tot,1,tot,1); if (ans == INF) printf("Case #%d: -1\n",++cas); else printf("Case #%d: %lld\n",++cas,ans); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。