1. 程式人生 > 其它 >[題解]UVA10917 Walk Through the Forest

[題解]UVA10917 Walk Through the Forest

UVA10917 Walk Through the Forest 題解

#1.0 理解題意

本題最需要理解的是這句話:

如果有一條從B到他的家的路線比從A到他的家的任何可能的路線都短,那麼他認為從A到B更好。

\(d_x\) 表示 \(x\)\(2\) 號節點的最短路,那麼上文的意思就是如果 \(A,B\) 間存在道路且 \(d_B<d_A\),那麼道路 \(A\to B\) 便是一種可行的選擇。

#2.0 大體思路

根據上面那句話,我們便需要先處理出所有點到 \(2\) 號點的最短距離。然後按照上面的要求重新建圖,這裡建出的邊是單向邊,再注意到得到的圖是一張有向無環圖\(\texttt{DAG}\)),因為每條邊的邊權都是非負數,用反證法不難證明得到的最短路圖不存在環。

此時考慮 \(\texttt{DAG}\) 上的動態規劃,設 \(sum_x\) 表示從 \(1\) 號節點到 \(x\) 號節點可行的路徑條數,顯然有

\[sum_x=\sum\limits_{(y,x)\in E}sum_y. \]

這裡 \(E\) 表示邊集,有序數對 \((u,v)\) 表示一條從 \(u\)\(v\) 的有向邊。邊界為 \(sum_1=1.\)

同時注意到,節點 \(1\) 不一定是入度為 \(0\) 的點,入度為零的點除 \(1\) 外不會對答案造成貢獻。

拓撲結束後得到的結果便是本題所求。

#define pii pair<int, int>
#define mp(a, b) make_pair(a, b)
#define mset(l, x) memset(l, x, sizeof(l))

const int N = 100010;
const int INF = 0x7fffffff;

struct Edge {
    int u, v, w;
    int nxt;
};
Edge e[N], ne[N];

int cnt = 1, ncnt = 1, head[N], nhead[N];
int dist[N], vis[N], tot[N], s, t, icnt[N];
int qx[N], frt, tal, tag[N], n, m;

priority_queue <pii > q;

inline void add(const int &u, const int &v, const int &w) {
    e[cnt].u = u, e[cnt].v = v, e[cnt].w = w;
    e[cnt].nxt = head[u], head[u] = cnt ++;
}

inline void addn(const int &u, const int &v) {
    ne[ncnt].u = u, ne[ncnt].v = v, icnt[v] ++;
    ne[ncnt].nxt = nhead[u], nhead[u] = ncnt ++;
}

void Dijkstra() {
    mset(dist, 0x3f);
    q.push(mp(0,2)); dist[2] = 0;
    while (q.size()) {
        int now = q.top().second; q.pop();
        if (vis[now]) continue;
        vis[now] = true;
        for (int i = head[now]; i; i = e[i].nxt)
          if (dist[e[i].v] > dist[now] + e[i].w) {
              dist[e[i].v] = dist[now] + e[i].w;
              q.push(mp(-dist[e[i].v], e[i].v));
          }
    }
}

inline void clear() {
    frt = 0, tal = -1; 
    ncnt = cnt = 1; mset(tot, 0);
    mset(icnt, 0); mset(tag, 0);
    mset(vis, 0); mset(dist, 0x3f);
    mset(head, 0); mset(nhead, 0);
}

void topo() {
    mset(vis, 0);
    for (int i = 1; i <= n; i ++) 
      if (!icnt[i] || i == 1) qx[++ tal] = i;
    tag[1] = tot[1] = vis[1] = 1;
    while (frt <= tal) {
        int now = qx[frt ++];
        for (int i = nhead[now]; i;i = ne[i].nxt) {
            icnt[ne[i].v] --;
            if (tag[now]) {
                tot[ne[i].v] += tot[now], 
                tag[ne[i].v] |= tag[now];
            }
            if (!icnt[ne[i].v] && !vis[ne[i].v]) {
                qx[++ tal] = ne[i].v;
                vis[ne[i].v] == true;
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    while (n) {
        scanf("%d", &m); s = 1, t = 2; clear();
        for (int i = 1; i <= m; i ++) {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            add(u, v, w); add(v, u, w);
        }
        Dijkstra();
        for (int i = 1; i <= n; i ++)
          for (int j = head[i]; j; j = e[j].nxt)
            if (dist[i] > dist[e[j].v])
              addn(i, e[j].v);
        topo();
        printf("%d\n", tot[t]);
        scanf("%d", &n);
    }
    return 0;
}

End

希望能為各位帶來幫助。