J牛牛的宝可梦Go【弗洛伊德Floyd+DP+剪枝】【2020牛客寒假算法基础集训营3】

题面

牛牛所在的W市是一个不太大的城市,城市有n个路口以及m条公路,这些双向连通的公路长度均为1。牛牛在玩宝可梦Go,众所周知呢,这个游戏需要到城市的各个地方去抓宝可梦,假设现在牛牛知道了接下来将会刷出k只宝可梦,他还知道每只宝可梦的刷新时刻、地点以及该宝可梦的战斗力,如果在宝可梦刷新时,牛牛恰好在那个路口,他就一定能够抓住那只宝可梦。

由于游戏公司不想让有选择恐惧症的玩家为难,所以他们设计不存在任何一个时刻同时刷出两只及以上的宝可梦。

假设不存在任何一个时刻会同时刷出两只宝可梦,牛牛一开始在城市的1号路口,最开始的时刻为0时刻,牛牛可以在每个时刻之前移动到相邻他所在位置的路口,当然他也可以保持原地不动,他现在想知道他能够捕获的宝可梦战斗力之和最大为多少?

 

样例一

3 2
1 2
2 3
3
1 1 5
2 3 10
3 2 1
11

样例二

1 0
3
1 1 100
100 1 10000
10000 1 1
10101

样例三

3 2
1 2
2 3
1
1 3 1000000000
0

样例四

3 2
1 2
2 3
1
1 2 1000000000
1000000000

首先,关于求解任意两点之间的最短路,是为了求解出任意两点之间相互达到所需要的最小花费时间。

然后的话,这里有个剪枝,除去剪枝的话,复杂度优化一点可以达到的是\large O(N * K * log(K)),这个复杂度是怎样来的呢?就是我们去求维护一个dp[点][时刻],然后我们每次因为只会更新一个点,所以我们可以去枚举N个点,包括自己,因为可以待着不动,我们用log的方法(二分查询)查询前面哪一个时刻可以转移到目前时刻,这是复杂度为\large O(N * K * log(K))的写法了。

其实会发现,这里有可以剪枝的部分,剪枝之后的复杂度会变成\large O(N * K),或者说是\large O(200 * K),因为图的最大的大小就是200,所以图的直径最长也不过是199,方便写,我们写到200,于是乎,200以上的距离我们可以任意到达,那么同样的,距当前询问有200次询问之前的询问,我们可以直接继承,所以这里可以维护一个前缀最大值。然后,我们每次只需要去看前1~200次中的询问,是否可以得到,以及跟最大值的比较,然后是更新答案。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define IINF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e2 + 7;
int N, M, K, head[maxN], cnt, mp[maxN][maxN], t[maxN];
ll dp[100005] = {0}, ans = 0;
inline void floyd()
{
    for(int k=1; k<=N; k++)
    {
        for(int j=1; j<=N; j++)
        {
            for(int i=1; i<=N; i++)
            {
                if(i == j) continue;
                mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
            }
        }
    }
}
struct node
{
    int t, p; ll val;
    node(int a=0, int b=0, ll c=0):t(a), p(b), val(c) {}
    inline void In_Put() { scanf("%d%d%lld", &t, &p, &val); }
    friend bool operator < (node e1, node e2) { return e1.t < e2.t; }
}op[100005];
int Lsan[100005];
int main()
{
    scanf("%d%d", &N, &M);
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=N; j++) mp[i][j] = INF;
        mp[i][i] = 0;
    }
    for(int i=1, u, v; i<=M; i++)
    {
        scanf("%d%d", &u, &v);
        mp[u][v] = mp[v][u] = 1;
    }
    floyd();
    scanf("%d", &K);
    for(int i=1; i<=K; i++)
    {
        op[i].In_Put();
        Lsan[i] = op[i].t;
    }
    sort(Lsan + 1, Lsan + K + 1);
    sort(op + 1, op + K + 1);
    op[0] = node(0, 1, 0);
    ll pre_max = 0;
    for(int i=1; i<=K; i++)
    {
        if(i > 200)
        {
            pre_max = max(pre_max, dp[i - 200]);
            dp[i] = op[i].val + pre_max;
        }
        else
        {
            dp[i] = -IINF;
        }
        for(int j=1; j<=200 && i - j >= 0; j++)
        {
            if(op[i].t - op[i - j].t >= mp[op[i].p][op[i - j].p])
            {
                dp[i] = max(dp[i], dp[i - j] + op[i].val);
            }
        }
        ans = max(ans, dp[i]);
    }
    printf("%lld\n", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值