競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 317C

ec

ABC317C

解法1: BitDP

ハミルトン閉路と同様に, 状態 \(\,(s,v)\,\) の 2つ を持って遷移. ここで,\(s \in 2^{N}\) は訪れた頂点の集合, \(v \in s\) は最後に訪れた頂点.
試験中はこの方針で考えていた. しかし,\(s\) しか状態に持っていなかったため WA. 例を作ることで,間違いを納得しやすかった. Star 型の tree を作って確認した.
例を作るのは,ソースコードを眺めるよりも早く解決する場合も多い. 急がば回れとも言える. 間違いを特定しやすい例を構築する能力は重要. 極端すぎず,暗算が出来る例. また,WA になりそうな場所のあたりがついていれば, それに合わせた例も構築しやすい.

解法2: DFS

訪れた頂点を記録しながら DFS. 実装も少なく,ミスしにくいので試験本番向き.
実装での注意は,DFS から帰るときに, 訪れた DFS で今見ていた頂点について, 訪れたという目印をリセットすること.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

// BitDP
int main() {
  ll n,m;
  cin >> n >> m;
  vvll cost(n, vll(n, -INFL));
  ll tot = 0;
  rep(i,m){
    ll a,b,c; cin >> a >> b >> c;
    --a, --b;
    cost[a][b] = c;
    cost[b][a] = c;
    tot += c;
  }

  ll ans = -INFL;
  rep(i,n){
    vvll dp(1LL<<n, vll(n, -INFL));
   
    dp[1LL<<i][i] = 0;
    rep(s,(1LL<<n)) rep(cu,n) if(s >> cu & 1){
      rep(ne, n) if(!(s >> ne & 1)){
        ll t = s | (1LL << ne);
        assert(t != s);
        chmax(dp[t][ne], dp[s][cu] + cost[cu][ne]);
      }
    }

    rep(s,(1LL<<n)) rep(ne,n) if(s >> ne & 1){
      chmax(ans, dp[s][ne]);
    }

  }
 
  cout << ans << endl;


  return 0;
}
int main() {
  ll n,m;
  cin >> n >> m;
  vvll cost(n, vll(n, -INFL));
  rep(i,m){
    ll a,b,c; cin >> a >> b >> c;
    --a,--b;
    cost[a][b] = c;
    cost[b][a] = c;
  }
 
  ll ans = -INFL;
  vll vis(n);
  auto dfs = [&](auto dfs, ll cu, ll dis = 0) -> void {
    if(vis[cu]) return;
    vis[cu] = true;
    chmax(ans, dis);

    rep(ne,n) if(cost[cu][ne] > 0){
      dfs(dfs, ne, dis+cost[cu][ne]);
    }

    vis[cu] = false;
  };

  rep(i,n){
    vis = vll(n);
    dfs(dfs,i);
  }
  cout << ans << endl;
 

  return 0;
}