题目描述
牛牛所在的W市是一个不太大的城市,城市有n个路口以及m条公路,这些双向连通的公路长度均为1,保证你可以从一个城市直接或者间接移动到所有的城市。牛牛在玩宝可梦Go,众所周知呢,这个游戏需要到城市的各个地方去抓宝可梦,假设现在牛牛知道了接下来将会刷出k只宝可梦,他还知道每只宝可梦的刷新时刻、地点以及该宝可梦的战斗力,如果在宝可梦刷新时,牛牛恰好在那个路口,他就一定能够抓住那只宝可梦。
由于游戏公司不想让有选择恐惧症的玩家为难,所以他们设计不存在任何一个时刻同时刷出两只及以上的宝可梦。
假设不存在任何一个时刻会同时刷出两只宝可梦,牛牛一开始在城市的1号路口,最开始的时刻为0时刻,牛牛可以在每个时刻之前移动到相邻他所在位置的路口,当然他也可以保持原地不动,他现在想知道他能够捕获的宝可梦战斗力之和最大为多少?
输入描述:
第一行输入两个正整数 表示城市的路口数目以及公路数目。
接下来m行每行两个正整数 表示一条长度为1链接两个路的公路。
接下来一行输入一个正整数 表示宝可梦的数目。
接下来输入k行,每行三个正整数 ,分别表示宝可梦刷新的时间、地点、以及战斗力,输入数据保证不会出现在同一时刻刷出多只宝可梦。
输出描述:
输出一个整数表示牛牛能捉到的宝可梦战斗力之和最大是多少。
输入
3 2
1 2
2 3
3
1 1 5
2 3 10
3 2 1
1 0
3
1 1 100
100 1 10000
10000 1 1
3 2
1 2
2 3
1
1 3 1000000000
3 2
1 2
2 3
1
1 2 1000000000
输出
11
10101
0
1000000000
备注:
输入数据保证不会出现在同一时刻刷出多只宝可梦。
题解
- 既然要在不同的城市移动,想要尽可能捕获多的宝可梦,那么就需要走最短路:
- 接下来我们需要捕捉宝可梦,很显然需要按照时间升序将宝可梦排序
- 定义
- 然后我们考虑状态转移,可以发现,对于 , ,如果 ,那么说明可以从 转移到 ,即可以更新
- 注意题目并不保证图联通,所以初始化地图
- 目前来看已经可以解决问题了,但是发现宝可梦数量 ,而 01背包dp过程复杂度 ,时间复杂度肯定是不允许的。
- 我们发现,地图点数
,因此任意两点最短路最多是199(为了好看当作200即可)。
考虑上图情况,如果 时间超过了200,说明我们在这段时间内,可以去到任意位置去找其他宝可梦,可以不从 直接到 ,那么我们知道01背包内层循环顺序是逆序的,也就是说,当我们判断 的时候,我们已经经过了 点,也就是说,我们已经得到了 的最优解,所以并不需要再次重复计算 。 - 但是我们可以直接让 的直接跳过么,很显然不可以,因为如果 ,但是实际上 的价值更优于 ;或者没有 供我们转移,那么我们就不可以直接跳过 。
- 但其实分析到这里的话,随便怎么围绕常熟优化一下都是可以 的,我采用的是每次找最靠近的 个宝可梦,这样复杂度就是 了。
AC-Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 7;
int dis[201][201];
void floyd(int n) {
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
struct Node {
int pos, t, val;
Node() {}
Node(int pos, int t, int val) :pos(pos), t(t), val(val) {}
}node[maxn];
ll dp[maxn];
int main() {
int n, m; while (cin >> n >> m) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
dis[i][j] = i == j ? 0 : 0x3f3f3f3f;
for (int i = 0; i < m; ++i) {
int u, v; cin >> u >> v;
dis[u][v] = dis[v][u] = 1;
}
floyd(n);
int k; cin >> k;
for (int i = 1; i <= k; ++i) {
cin >> node[i].t >> node[i].pos >> node[i].val;
dp[i] = -1e18;
}
sort(node + 1, node + 1 + k, [](Node a, Node b) {return a.t < b.t; });
node[0].pos = 1, node[0].t = 0; // 初始化牛牛位置
dp[0] = 0;
ll ans = 0;
for (int i = 1; i <= k; ++i) {
for (int j = i - 1; j >= 0; --j) {
if (i - j >= 200) break; // 常数优化
if (dis[node[i].pos][node[j].pos] <= node[i].t - node[j].t)
dp[i] = max(dp[i], dp[j] + node[i].val);
}
ans = max(ans, dp[i]);
}
cout << ans << endl;
}
return 0;
}