1. 程式人生 > 其它 >abc190E - Magical Ornament(bfs-狀壓dp)

abc190E - Magical Ornament(bfs-狀壓dp)

技術標籤:訓練動態規劃圖論

題目大意:

給你n個點m條邊
然後給出一組點集s
詢問最短的點集v包含s的所有的點且v的相鄰的點可以到達

題目思路:

在這裡插入圖片描述
可以看到k的值比較小
前幾天在oj剛寫過一個類似的k<=5
k<=5的時候只需要暴力列舉五個點的順序然後處理兩兩相鄰的最短路即可
這裡K<=17
第一步肯定都是 處理這個k個點的單源最短路
時間複雜度O(kn)
做題訓練賽邊權為1的圖寫了個堆優化被學長髮現了,所以這次直接就bfs上了 ,不過這個題我看學長程式碼的時候,他寫的堆優化(逃)
在這裡插入圖片描述
在這裡插入圖片描述
處理完單源最短路後
這裡k<=17 如果 全排列的話,時間複雜度是不允許的
17貌似又在暗示用二進位制

所以就狀壓dp
**

dp[i][j] 表示在i狀態下最後一個到達的點是j
方程為
dp[i|(1<<(en-1))][en] = min(dp[i|(1<<(en-1))][en],dp[i][st]+f[st][en]);

**

具體操作:

先列舉一種狀態
然後列舉這個狀態下為1的點作為最後一個到達的點
然後列舉這個狀態下為0的點作為下一次到達的點

Code:

int n,m,k,p[19],vis[maxn],dist[maxn],f[22][22],id[maxn],dp[(1<<18)][19],ans = inf;
vector<int
>e[maxn]; void bfs(int st) { rep(i,1,n) vis[i] = 0,dist[i] = inf; queue<int>q; q.push(st); dist[st]=0; vis[st]=1; while(q.size()) { int u=q.front(); q.pop(); for(int v:e[u]) { if(vis[v]) continue; dist[v] = dist[u]+1; q.push(v); vis[v]=1; } } rep(i,1,k) f[id[st]][i]
= f[i][id[st]] = dist[p[i]]; } ll qpow(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } int main() { n=read(),m=read(); for(int i=1 ; i<=m ; i++) { int u=read(); int v=read(); e[u].push_back(v),e[v].push_back(u); } k=read(); rep(i,1,k) p[i] = read(),id[p[i]] = i; rep(i,1,k) bfs(p[i]); rep(i,0,qpow(2,k)-1) rep(j,1,k) dp[i][j] = inf; rep(i,1,k) dp[1<<(i-1)][i] = 1; for(int i=0 ; i<=qpow(2,k)-1; i++) { for(int st=1 ; st<=k ; st++) { if((1<<(st-1))&i==0) continue; for(int en =1 ; en<=k ; en++) { if((1<<(en-1))&i) continue; dp[i|(1<<(en-1))][en] = min(dp[i|(1<<(en-1))][en],dp[i][st]+f[st][en]); } } } rep(i,1,k) ans = min(ans,dp[qpow(2,k)-1][i]); if(ans==inf) ans=-1; out(ans); return 0; }