首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

BZOJ 2165 大樓:倍增 Floyd 解決長度約束最短路

     編輯:關於C++

問題描述

給定有向圖,邊權為長度。求從 1 號點出發,總長度 ≥ K 的路徑中,經過最少邊數。n ≤ 100,K ≤ 1018。

倍增思想

預處理 f[p][i][j]:走 2p 步從 i 到 j 的最大長度。
遞推:f[p][i][j] = max_k (f[p-1][i][k] + f[p-1][k][j])(類似矩陣乘法,取 max 而非加)。

貪心構造答案

維护 g[i][j]:當前已走步數下,從 i 到 j 的最大長度。初始 g[i][i]=0,其餘 -INF。
從高位到低位嘗試加入 2p 步:
計算 t[i][j] = max_k (g[i][k] + f[p][k][j])
若 t[1][*] 全 < K,則確認加入這 2p 步,更新 g=t,答案加 2p。
最後再走一步即達標,答案 +1。

完整代碼

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 110;
const long long INF = 4e18;
long long f[62][MAXN][MAXN], g[MAXN][MAXN], t[MAXN][MAXN];
int n; long long K;

int main() {
    int T; cin >> T;
    while (T--) {
        cin >> n >> K;
        // 初始化
        for (int i=1; i<=n; ++i)
            for (int j=1; j<=n; ++j) {
                long long x; cin >> x;
                f[0][i][j] = x ? x : -INF;
            }
        for (int i=1; i<=n; ++i) f[0][i][i] = max(f[0][i][i], 0LL);
        // 倍增預處理
        int P = 0;
        while ((1LL<=0; --p) {
            // 計算 t = g * f[p]
            for (int i=1; i<=n; ++i)
                for (int j=1; j<=n; ++j)
                    t[i][j] = -INF;
            for (int k=1; k<=n; ++k)
                for (int i=1; i<=n; ++i)
                    for (int j=1; j<=n; ++j)
                        t[i][j] = max(t[i][j], g[i][k] + f[p][k][j]);
            // 檢查是否還需繼續
            bool need = true;
            for (int j=1; j<=n; ++j)
                if (t[1][j] >= K) { need = false; break; }
            if (need) {
                memcpy(g, t, sizeof(g));
                ans += 1LL<

關鍵點

  1. 「最大長度」而非最短路,因為要達到 ≥K,越長越容易滿足
  2. 倍增矩陣乘法:f[p] = f[p-1] ⊗ f[p-1](max-plus 半環)
  3. 貪心從高位嘗試:不超標就加入,類似二分答案構造
  4. -INF 用 4e18 避免溢出,memcpy 加速數組複製

此題是「倍增 + 矩陣乘法 + 貪心構造」的高級應用,將路徑長度約束轉化為步數最小化問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved