幸福路径
题目链接:luogu P4308
题目大意
给你一个有向图,点有点权,然后你一开始的体力是 1,某走一步体力会乘上一个小于 1 的小数,然后到每个点的分数是当前体力乘那个点的点权。
然后一条路径的分数是它每次经过点的分数和,然后要你求最大的路径分数保留一位小数的结果。
思路
你会发现数的值域很小,而且只用保留一位小数。
然后你发现随着乘,它的精度会越来越小,最后就无法影响答案。
那你就考虑暴力乘(那当然是不行的)。
然后你考虑加速这个过程,其实就是一个矩阵乘法(或者你直接就相当于倍增)
那(倍增不要钱),所以我们可以直接搞
100
100
100 次倍增,那它这个精度是按
log
\log
log 级别跑的所以也够了。
然后要小小注意的就是你不要先把一开始位置的贡献算上(这样就不好转移了),然后输出之前记得加回去。
然后你初始化是设成很大的负数,那因为可能就不动了,所以一开始从自己到自己费用是
0
0
0 而不是很大负数。
代码
#include<cstdio>
#include<iostream>
#define db double
using namespace std;
int n, m, v0, x, y;
db w[101], p, f[101][101][101], ans;
bool a[101][101];
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lf", &w[i]);
scanf("%d", &v0); scanf("%lf", &p);
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y); a[x][y] = 1;
}
for (int i = 1; i <= n; i++)//注意一开始可以根本不走
for (int j = 1; j <= n; j++)
if (i != j) f[1][i][j] = -1e100;
for (int i = 2; i <= 100; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
f[i][j][k] = -1e100;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[i][j])
f[1][i][j] = p * w[j];
db run = p;
for (int i = 2; i <= 100; i++) {//然后直接倍增转移
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
for (int l = 1; l <= n; l++) {
f[i][k][l] = max(f[i][k][l], f[i - 1][k][j] + f[i - 1][j][l] * run);
}
run = run * run;
}
for (int i = 1; i <= 100; i++) {
for (int j = 1; j <= n; j++) {
ans = max(ans, f[i][v0][j]);
}
}
printf("%.1lf", ans + w[v0]);//把一开始的加上(你没有算开头的)
return 0;
}