封面图源 【动漫作业本】:https://space.bilibili.com/488779255
[竞赛地址] https://ac.nowcoder.com/acm/contest/82345
E. Traffic Lights
给定n个点m条边。从s出发,目的地为t
每条边有需要花费的时间。
车每次从停止到启动需要花费5秒。
每个节点有红绿黄灯,只有绿灯和红灯可以通行。
问,到达t需要的最少时间。
数据范围
红绿灯持续时间
每条边花费时间
D. Merge Fruits
有n个水果,每个水果有特性ai,bi,合并左右两个相邻水果(ai,bi),(aj,bj)时,会释放ai*bj的能量,并得到新的水果(aj,bi)
现可以选择若干水果,将它们的权重乘以k,即修改后的水果(ai,bi),变为(k*ai,k*bi)
至少选择一个水果进行权值的修改。
问,将这n个水果合并成一个水果后(合并顺序任意),最多可以得到多少能量。
E. Traffic Lights(图论/最短路径/dijkstra)
就是最短路径加上了等红绿灯、车启动需要5秒的逻辑。
贪心策略还是保持不变,对于每个节点,尽量得取到达需要的最短时间。
如果此时处于红灯时间,则加上等待时间和车启动时间。
为什么取最短时间最优。因为,对于一个节点,越早到达,它的启动时间(处于绿灯和黄灯)就越早开启。也就是说,它总是不劣于任意到达时间更晚的时刻。
#define ll long long
#define inf 1e18
const int maxn = 10010;
#define pii pair<int, int>
#define f first
#define s second
#define mp make_pair
int n, m, s2, t;
int u, v, w;
int h[maxn][4]; // h[i][j], g, y, r的时间, h[i][4]为 g + y + r
vector<pii> ve[maxn];
ll dis[maxn];
bool vis[maxn];
void dijkstra() {
for (int i = 0; i < n; ++i) {
dis[i] = inf;
vis[i] = 0;
}
priority_queue<pii, vector<pii>, greater<pii> > q;
dis[s2] = 5; // 启动需要5秒
q.push(mp(dis[s2], s2));
while (!q.empty()) {
pii cur = q.top();
q.pop();
u = cur.s;
if (vis[u]) {
continue;
}
vis[u] = 1;
for (auto edge: ve[u]) {
v = edge.f;
w = edge.s;
int tmp = (cur.f + w) % h[v][3], d = cur.f + w;
// 到达该点,且准备好出发需要的时间
// 注意特判终点,此时无需启动
if (v != t && tmp >= h[v][0] + h[v][1]) {
d += h[v][3] - tmp + 5;
}
if (dis[v] > d) {
dis[v] = d;
q.push(mp(d, v));
}
}
}
}
void solve() {
for (int i = 0; i < n; ++i) {
h[i][3] = 0;
for (int j = 0; j < 3; ++j) {
scanf("%d", &h[i][j]);
h[i][3] += h[i][j];
}
ve[i].clear();
}
while (m--) {
scanf("%d%d%d", &u, &v, &w);
ve[u].push_back(mp(v, w));
ve[v].push_back(mp(u, w));
}
dijkstra();
printf("%lld:%02lld\n", dis[t] / 60, dis[t] % 60);
}
D. Merge Fruits(动态规划)
观察发现,没有添加权重时,原始的答案值为
a[1]*b[2]+a[2]*b[3]+...+a[n-1]*b[n]
添加了权值后,那么就相当于,上述的数列,添加上若干k或者k*k的系数。
容易发现这是一个线性dp
定义dpi表示第i项时,系数为k^j。
就可以愉快的递推了。
注意,题目要求至少改变一个点,所以我们需要单独特判下res = 只修改一个点的情况。
最后再从dpn, dpn,res中取最大值即可。
详见代码注释
#define ll long long
#define inf 1e18
const int maxn = 100010;
int n;
int a[maxn], b[maxn];
ll dp[maxn][3]; // 0, 1, 2
ll ans, k;
void solve() {
scanf("%d%lld", &n, &k);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i], &b[i]);
}
ll all = 0;
for (int i = 2; i <= n; ++i) {
all += a[i-1] * b[i];
}
ans = -inf;
for (int i = 1; i <= n; ++i) { // 特判 只改变 一个点的 答案值
ll tmp = a[i-1] * b[i] + a[i] * b[i+1];
ans = max(ans, all - tmp + k * tmp);
}
// init
dp[0][0] = 0;
dp[0][1] = dp[0][2] = -inf;
dp[1][0] = 0;
dp[1][1] = -inf; //
dp[1][2] = -inf;
++n; // 需要枚举到第n+1个点,不然会漏了情况
for (int i = 2; i <= n; ++i) {
dp[i][0] = dp[i-1][0] + a[i-1] * b[i];
dp[i][1] = dp[i-1][1] + a[i-1] * b[i];
dp[i][2] = dp[i-1][2] + k * k * a[i-1] * b[i];
// 第i-2个位置系数为2,那么此时第i-1个位置系数为1,第i个位置没有添加k,系数为0
dp[i][1] = max(dp[i][1], dp[i-2][2] + k * a[i-2] * b[i-1] + a[i-1] * b[i]);
// 第i-2个位置系数为0,第i个位置系数为2,此时第i-1个位置系数只能为1
dp[i][2] = max(dp[i][2], dp[i-2][0] + k * a[i-2] * b[i-1] + k * k * a[i-1] * b[i]);
}
ans = max(ans, max(dp[n][1], dp[n][2]));
printf("%lld\n", ans);
}
往期文章
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛(AIFBC):https://zhuanlan.zhihu.com/p/698684625
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛(GH):https://zhuanlan.zhihu.com/p/699108788
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛(J):https://zhuanlan.zhihu.com/p/699535119
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。