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;
}

 

發佈了799 篇原創文章 · 獲贊 960 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章