1. 程式人生 > 實用技巧 >Codeforces Round #693 (Div. 3) G. Moving to the Capital (圖,dp)

Codeforces Round #693 (Div. 3) G. Moving to the Capital (圖,dp)


  • 題意:有一張有向圖,每個點的權值為點\(1\)到該點的最短距離(每條邊的長度為\(1\)),對於一條路徑,這條路徑上最多隻能有一條邊,這條邊起點的權值不小於終點,現在要求每個點能到達路徑上的點的最小權值.
  • 題解:首先我們先用bfs求出每個點的權值,並且在求的同時用桶將點存起來,方便之後列舉權值的時候用,然後我們可以將權值從大到小列舉,記\(dp_i\)是當前這個點能到達路徑上的點的最小權值,對於當前的點\(u\)和它的出邊\(v\),如果\(dis[u] < dis[v]\),那麼我們是可以繼續隨便走的,所以當前狀態應該是\(dp[u]=min(dp[u],dp[v])\),否則,說明我們將第二次機會用掉了,之後就只能選擇第一種操作,所以我們更新的時候就不能將\(dp[v]\)
    (因為是從大到小列舉,所以\(dp[v]\)的狀態一定是已知的)更新給當前狀態,因為我們不知道\(dp[v]\)這個狀態是否還用了第二次操作,所以當前狀態就應該更新為\(dp[u]=min(dp[u],dis[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;
}