Codeforces Round #693 (Div. 3) G. Moving to the Capital (圖,dp)
阿新 • • 發佈:2021-01-19
- 題意:有一張有向圖,每個點的權值為點\(1\)到該點的最短距離(每條邊的長度為\(1\)),對於一條路徑,這條路徑上最多隻能有一條邊,這條邊起點的權值不小於終點,現在要求每個點能到達路徑上的點的最小權值.
- 題解:首先我們先用bfs求出每個點的權值,並且在求的同時用桶將點存起來,方便之後列舉權值的時候用,然後我們可以將權值從大到小列舉,記\(dp_i\)是當前這個點能到達路徑上的點的最小權值,對於當前的點\(u\)和它的出邊\(v\),如果\(dis[u] < dis[v]\),那麼我們是可以繼續隨便走的,所以當前狀態應該是\(dp[u]=min(dp[u],dp[v])\),否則,說明我們將第二次機會用掉了,之後就只能選擇第一種操作,所以我們更新的時候就不能將\(dp[v]\)
- 程式碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int t; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>t; while(t--){ int n,m; cin>>n>>m; vector<vector<int>> v(n+1); vector<vector<int>> tot(n+1); vector<int> dis(n+1,INF); vector<int> dp(n+1); int a,b; rep(i,1,m){ cin>>a>>b; v[a].pb(b); } queue<int> q; q.push(1); dis[1]=0; //bfs init while(!q.empty()){ int cur=q.front(); q.pop(); for(auto w : v[cur]){ if(dis[cur]+1<dis[w]){ dis[w]=dis[cur]+1; tot[dis[w]].pb(w); q.push(w); } } } rep(i,1,n){ dp[i]=dis[i]; } per(i,n-1,1){ for(auto u : tot[i]){ for(auto w : v[u]){ if(dis[w]>dis[u]) dp[u]=min(dp[u],dp[w]); else dp[u]=min(dp[u],dis[w]); } } } rep(i,1,n) cout<<dp[i]<<' '; cout<<'\n'; } return 0; }