[TJOI2013]黄金矿工

题目描述

小A最近迷上了在上课时玩《黄金矿工》这款游戏。为了避免被老师发现,他必须小心翼翼,因此他总是输。在输掉自己所有的金币后,他向你求助。每个黄金可以看做一个点(没有体积)。现在给出你N个黄金的坐标,挖到它们所需要的时间以及它们的价值。有些黄金在同一条直线上,这时候你必须按顺序挖。你可以瞬间把钩子转到任意角度。请你帮助小A算出在时间T内他最多可以得到多少价值的金子。

输入输出格式

输入格式:

 

第一行,两个整数N和T,表示黄金的个数和总时间。接下来N行,每行四个整数x,y,t,v分别表示黄金的坐标,挖到这个黄金的时间,以及这个黄金的价值。(0≤|x|≤200,0<y≤200,0<t≤200,0≤v≤200)

 

输出格式:

 

一个整数,表示你可以在T时间内得到的最大价值。

 

输入输出样例

输入样例#1: 

3 10
1 1 1 1
2 2 2 2
1 3 15 9

输出样例#1: 

3

输入样例#2: 

3 10
1 1 13 1
2 2 2 2
1 3 4 7

输出样例#2: 

7

说明

30%的数据,0 < T ≤ 4000

 

如果在同一条直线上要先选第一个,那不就可以连接成一条链,几条链连起来不就是树形dp吗

于是我果断开始打树形dp,其实这也没有问题

但是树形dp打到一半,发现根本就不需要树形dp,因为只有更节点才有多个子树

其他的最多只有一个孩子,于是我就把这些子树变成一条链

每一条链用一个一维数组来存,并记录一下这条链中到达第i个的时间花费和价值,再用dp一次解决吧

下面解释一下如何判断是否在一条直线上

我记得有一个关于直线的公式  y = k x + b

然而这里是从原点出发所以没有b 也就是  y = k x 

其中k是斜率,如果斜率相同就是在同一条直线上(都是整数点所以不考虑精度问题)

k = y / x , 于是我们把每一个坐标的k取出来,并且排序一下,就可以很轻松的找出来啦

参考代码(可以加很多优化)

// luogu-judger-enable-o2
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std ;

const int N = 2e2 + 10 ;

struct node {
    int x , y , t , v ;
    double k ;
} a[N] ; int n , T ;
inline bool cmp ( node n1 , node n2 ) {
    if ( n1.k != n2.k ) return n1.k < n2.k ;
    return n1.y < n2.y ; 
}

int tot , g[N][N][2] ;
int dp[N*N] ; 
//g[][][0] 表示 时间
//g[][][1] 表示 价值 
int main() {
    cin >> n >> T ;
    for ( int i = 1 ; i <= n ; i ++ ) {
        cin >> a[i].x >> a[i].y >> a[i].t >> a[i].v ;
        a[i].k = double( a[i].y ) / a[i].x ;
    }
    sort ( a + 1 , a + n + 1 , cmp ) ;
    tot = 1 ; g[1][1][0] = a[1].t ; 
    g[1][1][1] = a[1].v ; g[1][0][0] = 1 ;
    for ( int i = 2 ; i <= n ; i ++ ) {
        if ( a[i].k != a[i-1].k ) {
            tot ++ ; g[tot][1][0] = a[i].t ;
            g[tot][1][1] = a[i].v ; g[tot][0][0] = 1 ;
            continue ;
        }
        int tt = ++ g[tot][0][0] ;
        g[tot][tt][0] = g[tot][tt-1][0] + a[i].t ;
        g[tot][tt][1] = g[tot][tt-1][1] + a[i].v ;
    }
    for ( int i = 1 ; i <= tot ; i ++ ) 
        for ( int j = T ; j >= 0 ; j -- ) 
            for ( int k = 1 ; k <= g[i][0][0] ; k ++ ) {
                if ( j >= g[i][k][0] ) dp[j] = max ( dp[j] , dp[j-g[i][k][0]] + g[i][k][1] ) ;
                else break ;
            }
    cout << dp[T] << endl ; return 0 ;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值