发布于 ,更新于 

细菌 题解

题意

一张有向无环带权图,有一个初始值 \(k\),从入口进入后每过一条边 \(i\)\(k = k \times 2+w_i\)\(w_i\) 为边权,有正有负),要求从入口到出口的过程中始终 \(k>0\),求使方案存在的最小初始值 \(k\)

思路

之前什么答辩题面

先忽略 \(k\) 经过一条边时翻倍,只考虑加边权,如何求出答案?由于图带边权,保证不出现环,同时判断一个 \(k\) 值是否合法的方式实际上是计算一条路径边权和并与 \(k\) 比较,联想到最短/长路。

新题面中提到只需存在一种方案能让出口 \(k>0\) 即可,显然是最短路。但是如果从起点求最短路还要考虑 \(k\) 的影响,因此反向建图,终点初始值设为 \(1\),从终点跑即可。

由于边权有正有负,只能跑 SPFA。

再考虑 \(k\) 的变换还有一个 \(\times 2\),因此跑反向的 SPFA 松弛边时需要把新算出的 \(dist_i\) 值除以 \(2\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <bitset>
#include <cstdio>
#include <cstring>
#include <queue>

// 模板函数可以参考博文 https://blog.tibrella.top/post/2023-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7%E6%9D%82%E8%B0%88%E4%B8%8E%E5%B8%B8%E6%95%B0%E4%BC%98%E5%8C%96/
template <typename Type>
void read(Type& _x) {
_x = 0;
bool f;
char ch;
f = 0;
do {
ch = getchar();
if (ch == '-') f = 1;
} while (ch < 48 || ch > 57);
do {
_x = (_x << 3) + (_x << 1) + (ch ^ 48);
ch = getchar();
} while (ch > 47 && ch < 58);
_x = f ? -_x : _x;
}

template <typename Type>
Type max(Type _a, Type _b) {
return _a > _b ? _a : _b;
}

using std::bitset;
using std::queue;

#define N 105

struct edge {
edge* nex;
int u, v, w;
} graph[N * N];
edge* fir[N];
int dis[N];
edge* idx = graph;
int n;

queue<int> q;
bitset<N> st;

void add(int u, int v, int w) {
++idx;
idx->u = u;
idx->v = v;
idx->w = w;
idx->nex = fir[u];
fir[u] = idx;
}

void spfa(int nod);

int t1;

int main() {
read(n);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
read(t1); // 中转变量
if (t1) add(j, i, -t1); // 反向存图
}
}

spfa(n);

printf("%d", dis[1]);

return 0;
}

void spfa(int nod) {
st.reset();
memset(dis, 0x3f, sizeof dis);
dis[nod] = 1;
st[nod] = 1;
q.push(nod);

while (!q.empty()) {
nod = q.front();
q.pop();
st[nod] = 0;
for (edge* e = fir[nod]; e; e = e->nex) {
if (dis[e->v] > max(1, (dis[nod] + e->w + 1) / 2)) { // +1 防止除法向下取整造成错误
dis[e->v] = max(1, (dis[nod] + e->w + 1) / 2);
if (!st[e->v]) {
q.push(e->v);
st[e->v] = 0;
}
}
}
}
}