NOIP 2017 逛公園 最短路 dp

2022-05-01 03:33:06 字數 3915 閱讀 5595

題意:

求出 從 \(1\) 到 \(n\) 的路徑長度小於等於最短路 \(+k\) 的路徑個數

分析:首先觀察資料特點

\(30\%\) 的資料 \(k=0\) 且沒有 \(0\) 邊,等同於最短路計數

\(70\%\) 的資料沒有 \(0\) 邊,那麼就是相當於沒有 \(0\) 環

\(100\%\) 的資料 \(k \le 50\)

首先考慮第一檔:

顯然就是乙個最短路徑計數,是乙個非常經典的問題,就是乙個在最短路圖上 \(dp\) 的問題,非常基礎

再考慮第二檔:

沒有 \(0\) 環,那麼等同於滿足 \(dp\) 沒有後效性,我們可以把 \(dp\) 狀態稍微改一下

設 \(dis\) 表示最短路

\(dp[i][j]\) 表示到達 \(i\) 當前長度為 \(dis[i]+j\) 的路徑個數

\(dp[i][j] \rightarrow dp[to][j + e[i].w + dis[i] - dis[to]]\)

就完成了

那麼對於第三檔

我們需要處理零環

一種比較簡單的方法可以見同學部落格 鏈結

在這裡講一種比較巧妙的方法(**也比較短

考慮反圖處理

把圖反過來連建,然後跑 \(dijkstra\) 就可以判斷哪些點是不通的

然後處理完這個我們通過 \(dis\) 陣列把每條邊的權值更改成為冗餘代價

然後再對新圖跑一遍 \(dijkstra\) 就可以判斷出一些點到達終點肯定是會大於 \(k\) 的

再對新圖跑一邊拓撲排序求出拓撲序順便搞掉環

然後這些都搞完了我們就沒有環了

之後就可以按照原來的方法 \(dp\) 了

大概演算法就差不多了

實際寫**時為了方便統計答案我們在 \(dp\) 狀態後多一維表示最後這一次更新用的是最短路圖上的數最短路樹上的邊還是不是

因為最後一次沒有統計,那麼我們就要求都是樹邊

大功告成

洛谷上跑的飛快

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std ;

//#define int long long

#define rep(i, a, b) for (int i = (a); i <= (b); i++)

#define per(i, a, b) for (int i = (a); i >= (b); i--)

#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)

#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)

#define clr(a) memset(a, 0, sizeof(a))

#define ass(a, sum) memset(a, sum, sizeof(a))

#define lowbit(x) (x & -x)

#define all(x) x.begin(), x.end()

#define ub upper_bound

#define lb lower_bound

#define pq priority_queue

#define mp make_pair

#define pb push_back

#define fi first

#define se second

#define iv inline void

#define enter cout << endl

#define siz(x) ((int)x.size())

#define file(x) freopen(#x".in", "r", stdin),freopen(#x".out", "w", stdout)

typedef long long ll ;

typedef unsigned long long ull ;

typedef pair pii ;

typedef vector vi ;

typedef vector vii ;

typedef queue qi ;

typedef queue qii ;

typedef set si ;

typedef map mii ;

typedef map msi ;

const int n = 100010 ;

const int m = 200010 ;

const ll inf = 1ll << 60 ;

const int iinf = 1 << 30 ;

const ll linf = 2e18 ;

const double eps = 1e-7 ;

void print(int x)

void print(string x)

void douout(double x)

template void chmin(t &a, t b)

template void chmax(t &a, t b)

int n, m, k, mod, top, num, ans, t ;

int head[n], pre[n], vis[n], int[m], bad[n], t[n], deg[n], dp[53][n][2] ;

ll dis[n] ;

struct node g[n << 1] ;

struct edge e[n << 1] ;

struct point

} ;void add(int a, int b, int w) ;

head[a] = top ;

}void dij(int s) ) ;

while (!q.empty()) ) ;

}} }

}bool topsort()

while (l < r)

} }return (num == r) ;

}void init()

void main() ;

add(b, a, c) ;

} dij(n) ;

rep(i, 1, n) if (dis[i] == inf) bad[i] = 1 ;

rep(i, 1, n - 1) if (dis[i] != inf) int[pre[i]] = 1 ;

clr(head) ; top = 0 ;

rep(i, 1, m)

dij(1) ;

rep(i, 1, n) if (dis[i] > k) bad[i] = 1 ;

if (!topsort())

clr(dp) ;

dp[0][1][0] = 1 ;

rep(i, 0, k) }}

} }ans = 0 ;

rep(i, 0, k)

rep(j, 1, num)

(ans += dp[i][t[j]][0]) %= mod ;

printf("%d\n", ans) ;

}signed main()

/*寫**時請注意:

1.ll?陣列大小,邊界?資料範圍?

2.精度?

3.特判?

4.至少做一些

1.最大值最小->二分?

2.可以貪心麼?不行dp可以麼

3.可以優化麼

4.維護區間用什麼資料結構?

5.統計方案是用dp?模了麼?

6.逆向思維?

*/

NOIP 2017 逛公園(最短路 記憶化搜尋)

肯定要先跑一次最短路 題目中的k 相當於允許我們走k距離的 冤枉路 回想之前有些題是如何判斷哪些邊是屬於最短路上的 當dis now edge u val dis vis 這條邊就在最短路上 類似的 我們可以得出 dis now edge u val dis vis 就是這一次走的 冤枉路 的長度 ...

比賽 NOIP2017 逛公園

考試的時候靈光一閃,瞬間推出dp方程,但是不知道怎麼判 1,然後?然後就炸了。後來發現,我只要把拓撲和dp分開,中間加乙個判斷,就ac了,可惜。看這道題,我們首先來想有哪些情況是 1 只要有零環在滿足題目要求的路徑上,那麼這條路徑就可以不停地走,於是就 1了。如何判有沒有零環呢?機械化地兩遍不同方向...

NOIP2017 洛谷 逛公園

初見安 這個本狸咕了4個月的題終於寫出來了qaq!這裡是傳送門 洛谷p3953 逛公園 策策每天都會去逛公園,他總是從1號點進去,從nn號點出來。策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是乙個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到...