Road Removal
先に重要じゃない都市同士を結ぶ道路をすべて採用します。するとグラフが、重要じゃない都市のみで構成される複数の連結成分と複数の重要な都市から成り立つ森になります。前者の連結成分を一つの節点とみなしてクラスカル法のようなことをすればOK。使用するアルゴリズムや実装は簡単だけどどのアルゴリズムを適用すれば良いのかが見えにくいという問題で、個人的には良問だと思います。
- const int MAX = 10000;
- int par[10000];
- int rank[10000];
- void init(int n) {
- REP(i, n) {
- par[i] = i;
- rank[i] = 0;
- }
- }
- int find(int x) {
- if (x == par[x])
- return x;
- return par[x] = find(par[x]);
- }
- bool same(int x, int y) {
- return find(x) == find(y);
- }
- void merge(int x, int y) {
- x = find(x);
- y = find(y);
- if (x == y)
- return;
- if (rank[x] > rank[y]) {
- par[y] = x;
- } else {
- par[x] = y;
- if (rank[x] == rank[y])
- rank[y]++;
- }
- }
- void solve() {
- int T;
- scanf("%d", &T);
- REP(t, T) {
- int N, M, K;
- scanf("%d %d %d", &N, &M, &K);
- init(N);
- int a, b;
- vector<pair<int, int> > road;
- REP(i, M) {
- scanf("%d %d", &a, &b);
- if (min(a, b) >= K)
- merge(a, b);
- else
- road.push_back(MP(a, b));
- }
- int ret = 0;
- REP(i, road.size()) {
- a = road[i].first;
- b = road[i].second;
- if (same(a, b))
- ++ret;
- else
- merge(a, b);
- }
- printf("Case #%d: %d\n", t+1, ret);
- }
- }
Monopoly
Union Findと動的計画の合わせ技です。dp[i][j]に会社iの社長が直属の部下をj人持つときの木の高さの最小値を保持します。あとポイントとなるのは、dp[i][j]の更新を高速に計算するためにminHeight[i]に現時点における会社iが取り得る木の高さの最小値を保存しておくことです。Union Findの一度の操作をO(log(N))とすると、一つの問題はO(N log(N) + ND)で計算できます。
- const int INF = 999999999;
- const int MAX_N = 30000;
- const int MAX_D = 5000;
- int dp[MAX_N][MAX_D+1];
- int minHeight[MAX_N];
- int par[MAX_N];
- int rank[MAX_N];
- void init(int n) {
- REP(i, n) {
- par[i] = i;
- rank[i] = 0;
- }
- }
- int find(int x) {
- if (x == par[x])
- return x;
- return par[x] = find(par[x]);
- }
- void merge(int x, int y) {
- x = find(x);
- y = find(y);
- if (x == y) return;
- if (rank[x] > rank[y])
- par[y] = x;
- else {
- par[x] = y;
- rank[y]++;
- }
- }
- bool same(int x, int y) {
- return find(x) == find(y);
- }
- void solve() {
- int T;
- scanf("%d", &T);
- REP(t, T) {
- int N, D;
- scanf("%d %d", &N, &D);
- init(N);
- REP(i, N) REP(j, D+1) dp[i][j] = INF;
- REP(i, N) {
- minHeight[i] = 1;
- dp[i][0] = 1;
- }
- int a, b;
- REP(i, N-1) {
- scanf("%d %d", &a, &b);
- a = find(a-1);
- b = find(b-1);
- merge(a, b);
- int x = find(a);
- dp[x][0] = INF;
- for (int j = D; j > 0; j--)
- dp[x][j] = min(max(dp[a][j-1], 1+minHeight[b]), // a becomes the president
- max(dp[b][j-1], 1+minHeight[a])); // b becomes the president
- minHeight[x] = INF;
- REP(j, D+1)
- minHeight[x] = min(minHeight[x], dp[x][j]);
- }
- int ret = INF;
- int x = find(0);
- REP(i, D+1)
- ret = min(ret, dp[x][i]);
- printf("Case #%d: %d\n", t+1, ret);
- }
- }
0 件のコメント:
コメントを投稿