细菌 题解
题意
一张有向无环带权图,有一个初始值 \(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>
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)) { dis[e->v] = max(1, (dis[nod] + e->w + 1) / 2); if (!st[e->v]) { q.push(e->v); st[e->v] = 0; } } } } }
|