読者です 読者をやめる 読者になる 読者になる

UVa12527 Kingdoms

問題
首都(ノード1)から他の都市(ノード2〜n)にアクセス可能なように辺を建設する。各辺に建設コストがあり、建設に使える総コストは K 以内に収める必要がある。各ノードに人口が与えられるので、首都にアクセス可能な人口を最大化せよ。

解法
アクセス可能なノードをビットで持ち、そのビットの状態をノードとしたグラフ上をダイクストラする。

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

#define MAX_N (1000)
#define INF (1<<29)
int N, M, K;

struct Edge {
  int to, cost;
};

typedef pair<int, int> Pii;
typedef pair<int, Pii> Piii;

vector<Edge> G[MAX_N];
int city[MAX_N];

int solve() {
  
  int ans = 0;
  int dist[1<<N];
  fill(dist, dist+(1<<N), INF);
  dist[1<<0] = 0;
  priority_queue<Pii> pq;
  pq.push(Pii(dist[1<<0], 1<<0));
  while(!pq.empty()) {
    int const cost = pq.top().first;
    int const nowS = pq.top().second;
    pq.pop();
    int sum = 0;
    for(int n=0; n<N; n++) {
      if(!(nowS >> n & 1)) continue;
      sum += city[n];
      for(int i=0; i<G[n].size(); i++) {
        Edge &e = G[n][i];
        int next = nowS|(1<<e.to);
        if(dist[next] <= cost+e.cost) { continue; }
        if(cost+e.cost > K) { continue; }

        dist[next] = cost+e.cost;
        pq.push(Pii(dist[next], next));
      }
    }
    ans = max(ans, sum);
  }
  
  return ans;
}

void ini() {
  for(int i=0; i<N; i++) G[i].clear();
}

int main() {
  
  int Tc; cin >> Tc;
  while(Tc--) {
    ini();
    cin >> N >> M >> K;
    for(int i=0; i<N; i++) { cin >> city[i]; }
    for(int i=0; i<M; i++) {
      int u, v, c; cin >> u >> v >> c;
      u --, v --;
      G[u].push_back((Edge){v, c});
      G[v].push_back((Edge){u, c});
    }
    
    cout << solve() << endl;
  }
  
  return 0;
}

メモ
初めに最小全域木やUnionFindの臭いがすると思った。感覚で何かのアルゴリズムを適用出来ないかと考えても、論理がたどり着かない場合は大抵解答できないので、臭いに従わず考えなおした。チームの人が〜〜の貪欲的な処理で解けるだろうという事を言ったような気がする。何らかの貪欲的な処理が必要だろうという気はしていたが、下手にやると WA だろうという感覚はあった。すぐにチームの人が、ダイクストラでも良いとと言った。この時点でチームの人はビットの状態をノードとして持ったダイクストラのイメージはできていた。自分はダイクストラという感覚はあった。ダイクストラは貪欲的な処理をしているので、何らかの貪欲的な処理が必要という感覚はこのことかと考えた。感覚で解法はダイクストラの方が良いのではないか、と言った。少し待ってからチームの人がダイクストラのほうが良いと言った。自分はダイクストラでどのように解くのか、と改めて考えた。まだビットの状態を持つというイメージができていなかったので、都市でダイクストラしたら失敗ではないかとチームの人に言ったがチームの人がコードを書きだして解答の真意を察した。ここでダイクストラの計算量O(|E|logV)に対して、エッジの数は問題ではないのかと考えた。しかしそれほど多くはならないのではないか?と考えなおした。