POJ 1769

要用线段树的dp

题目描述:

一堆m~5e5可已到达[l,r]的东西,不能改变顺序的从中挑选一些,使得1个在1的东西能够交换到n~5e4.

题解:

首先是dp[i][j],考虑完i个机器,1能够移动到j所花费的最小的长度.然后看每个机器怎么去更新,对于i:l,r, 首先拿出来dp[i-1][l]的值,一定是l到r最小的,然后用i,拿这个值去修改一下dp[i][l][r]的最小值,[i]可省略,用线段树来成端更新.
一个更好的思路:考虑每一个及其的更新作用.其实lr机器的作用是,将l到r的东西能够到r,这是它基本的功能.然后从l到r中选出最小的到r,就是可能因为它才能提供的能够到r的最好值.更新的思维很重要.(考虑最后一个机器就行了).

重点:

获取状态可以用最小来描述,然而更新如果对一段区间都有影响的话,那么就必须用线段树了.
另解:考虑每个机器的作用,到r就是r,r之前的不管.但是获取l到r要遍历一遍取最值.

代码:

还有一道题目很好.
poj3171

牛来清理牧场,牛有区间覆盖牧场,并且牛无顺序,排序和线段数。
其实就是区间覆盖最小代价.
题解:dp[i]是覆盖到i的代价.然后看每一个区间的更新,ab,花费s,则从a和b中一个最小的,更新到b.然后为了没有后效性,先把牛按照b来排个序.

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)
typedef long long ll;

using namespace std;

const ll maxn = 1e6 + 100;
const ll M_N = 1e6 + 0.1;
const ll INF = 10000000000000000L;
struct info
{
    ll t1, t2, s;
};
info niu[maxn];
ll a[maxn];

ll n, m, e;

void push_up(ll rt)//线段树快速查询
{
    a[rt] = min(a[rt*2], a[rt*2 + 1]);
}
void update(ll q, ll key, ll l, ll r, ll rt)
{
    if(l == r)
    {
        a[rt] = key;
        return;
    }
    ll m = (l +r)/2;
    if(q <= m)
    {
        update(q, key, l, m, rt*2);
    }
    else
    {
        update(q, key, m + 1, r, rt*2 + 1);
    }
    push_up(rt);
}
ll query(ll L, ll R, ll l, ll r, ll rt)
{
    if(L <= l && R >= r)
    {
        return a[rt];
    }
    ll ans = INF;
    ll m = (l + r)/2;
    if(L <= m)
    {
        //printf("--%d\n", rt);
        ans = min(ans, query(L, R, l, m, rt*2));
    }
    if(R >= m + 1)
    {
        ans = min(ans, query(L, R, m + 1, r, rt*2 +1));
    }
    return ans;
}

bool cmp(info a, info b)
{
    if(a.t2 == b.t2)
    {
        return a.t1 < b.t1;
    }
    return a.t2 < b.t2;
}

void solve()
{
    sort(niu, niu + n, cmp);/先排序,基础
    REP_D(i, 0, M_N)
    {
        a[i] = INF;
    }
    REP(i, 0, n)
    {
        ll temp = query(niu[i].t2, niu[i].t2, 1, e, 1);//这个写的还很嫩啊.其实不用这个temp
        if(niu[i].t1 == m)//初始化把dp[m]=0,就不需要这个了
        {
            temp = min(temp, niu[i].s);
            //temp = min(temp, query(niu[i].))
        }
        else
        {
            temp = min(temp, query(niu[i].t1 - 1, niu[i].t2 - 1, 1, e, 1) + niu[i].s);
        }
        update(niu[i].t2, temp, 1, e, 1);
    }
    ll ans = query(e, e, 1, e, 1);
    if(ans != INF)
        printf("%lld\n", ans);
    else
        printf("-1\n");
}

int main()
{
    //freopen("4Din.txt", "r", stdin);
    //freopen("4Dout.txt", "w", stdout);
    while(scanf("%lld%lld%lld", &n, &m, &e) != EOF)
    {
        m++;
        e++;
        REP(i, 0, n)
        {
            scanf("%lld%lld%lld", &niu[i].t1, &niu[i].t2, &niu[i].s);
            niu[i].t1++;
            niu[i].t2++;
        }
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值