AtCoderビギナーコンテスト190E-魔法の飾り

トピックリンク:ここをクリック〜

トピック

  • n個の宝石、m個の関係ai、biは、宝石aiとbiを隣接して配置できることを示し、k個の宝石にはciの番号が付けられます。
  • すべてのk個の宝石が含まれ、使用される宝石の総数が最小になるように、宝石を合理的にひもでつなぐ方法を尋ねます
  • 範囲1≤N≤1e5、0≤M≤1e5、1≤ai <bi≤N、1≤K≤17、1≤Ci≤N
  • mは関係を繰り返さない

アイデア

  • bfs +状態圧力dp
  • これらのm個の関係はm個のエッジと見なすことができ、エッジの重みは1です。次に、すべてのk点ciが実行されるように最短経路が構築されます。
  • k bfsを実行し、dis [i] [x]を記録して、点c [i]から点xまでの最短経路を示します。
  • 次のタスクは、すべてのk点を実行することです。それらが正しい場合は、それらを配置する必要があります。17の階乗計算は大きすぎて、tになります。
  • 次に、動的計画法、状態圧力dp、バイナリ状態の転送、dp [t] [i]は、tの状態の点c [i]への最短経路を表します。
  •  次に、k個の点で渡されていない点jを見つけ、点jを追加した後の状態がt +(1 << j)である場合、伝達方程式はdp [t +(1 << j)] [j]です。 = max(dp [t +(1 << j)] [j]、dp [t] [i] + dis [i] [c [j]]);
  • 最終状態はバイナリk1s、つまり(1 << k)-1であり、dp [(1 << k)-1] [i]の最小値を更新します。

ACコード

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int dis[20][maxn], c[20];
int dp[1<<17][20];
vector<int> v[maxn];
void bfs(int i){ //bfs跑最短路
    int st = c[i];
    queue<int> q;
    q.push(st);
    dis[i][c[i]] = 0;
    while(q.size()){
        int t = q.front(); q.pop();
        for(auto it : v[t]){
            if(dis[i][it] == inf){
                dis[i][it] = dis[i][t] + 1;
                q.push(it);
            }
        }
    }
}
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i ++){
        int x, y;
        scanf("%d%d", &x, &y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    int k; scanf("%d", &k);
    for(int i = 0; i < k; i ++) scanf("%d", &c[i]);
    memset(dis, inf, sizeof(dis));
    memset(dp, inf, sizeof(dp));
    for(int i = 0; i < k; i ++){
        bfs(i);
    }
    for(int i = 0; i < k; i ++){//初始点距离是0
        dp[1<<i][i] = 0;
    }
    for(int t = 0; t < (1 << k); t ++){
        for(int i = 0; i < k; i ++){
            if(!((t >> i) & 1)) continue; //说明状态t中没有k个点中的第i个点,我们要跑到第i个点,所以这个状态不符合
            for(int j = 0; j < k; j ++){
                if((t >> j) & 1) continue;//说明状态t中有k个点中的第j个点, 现在要走到新点,所以跳
                dp[t | (1 << j)][j] = min(dp[t | (1 << j)][j], dp[t][i] + dis[i][c[j]]); //dis表示k个点中第i个点跳到第j个点的最短路径
            }
        } 
    }
    int ans = inf;
    for(int i = 0; i < k; i ++){
        ans = min(ans, dp[(1<<k) - 1][i] + 1); //算的是宝石的数量,我们求的是边的数量,所以还要加上初始点
    }
    if(ans == inf) ans = -1;
    printf("%d\n", ans);
    return 0;
}

 

おすすめ

転載: blog.csdn.net/weixin_43911947/article/details/113486302