题目链接:http://acm.uestc.edu.cn/problem.php?pid=1558
题目大意:给定n个区间[begi,endi],表示能从begi转换到endi,每个区间都有一个权值ti,现在要从1转换到大等于m的一个状态,问最少的权值,如果没办法到达那个状态,输出-1。n<=10万。
题目大意:给定n个区间[begi,endi],表示能从begi转换到endi,每个区间都有一个权值ti,现在要从1转换到大等于m的一个状态,问最少的权值,如果没办法到达那个状态,输出-1。n<=10万。
解题思路:线段树优化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原创,但可以转载,因为我们是兄弟。
针对给定的大量区间转换问题,通过线段树优化动态规划算法来寻找从初始状态到目标状态的最小权值路径。介绍了问题背景、解题思路及核心代码实现。
809

被折叠的 条评论
为什么被折叠?



