SRM629D1Med CandyCollection
考察に手間取った
問題概要(原文)
$N$ 種類のガチャと $N$ 枚のカードがある.種類 $i$ のガチャには種類 $p,q (p \neq q)$ のカードがそれぞれ $a,b$ 枚入っている. ここで,どの種類のカードもちょうど $2$ 種類のガチャからしか手に入らないことが保証されている.
$N$ 種類のカード全てを $1$ 枚以上手に入れたい.何回ガチャを回せば十分か?
考察
$1$ 種類のガチャには $2$ 種類のカードしかない.つまり,種類 $i$ のガチャから $p,q$ に無向辺を張ると, 閉路が出来上がる.
閉路 $1$ つに着目する. 端の買い方を決め打ちする. 遷移は以下の $4$ 通り
- 両方のカードが当たるまで引く
- ここのガチャで当てないと,もう手に入らないカードが当たるまで引く
- 次のガチャでも引けるカードが当たるまで引く
- 買わない
これを適切に試して,全てのカードが買われているようにする最小値を求める.
これを全ての閉路について適切にやればよい. $O(N)$ でもできるが,$O(N^2)$ でやった.
ソースコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Debug = System.Diagnostics.Debug;
using StringBuilder = System.Text.StringBuilder;
public class CandyCollection
{
public int solve(int[] t1, int[] num1, int[] t2, int[] num2)
{
var n = t1.Length;
var ans = 0;
var used = new bool[n];
for (int i = 0; i < n; i++)
{
if (used[i]) continue;
used[i] = true;
var dp = new int[2, 2];
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
dp[j, k] = 1000000000;
dp[0, 0] = 0;
dp[0, 1] = num1[i] + 1;
dp[1, 0] = num2[i] + 1;
dp[1, 1] = Math.Max(num1[i], num2[i]) + 1;
var c = t2[i];
var p = -1;
for (int j = 0; j < n; j++)
if (used[j]) continue;
else
{
if (t1[j] == c) { p = j; break; }
else if (t2[j] == c)
{
Swap(ref t1[j], ref t2[j]);
Swap(ref num1[j], ref num2[j]);
p = j; break;
}
}
for (; p != -1;)
{
used[p] = true;
var next = new int[2, 2];
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
next[j, k] = 1000000000;
for (int j = 0; j < 2; j++)
{
next[j, 0] = Math.Min(dp[j, 1], dp[j, 0] + num2[p] + 1);
next[j, 1] = Math.Min(dp[j, 1] + num1[p] + 1, dp[j, 0] + Math.Max(num1[p], num2[p]) + 1);
}
dp = next;
c = t2[p];
p = -1;
for (int j = 0; j < n; j++)
if (used[j]) continue;
else
{
if (t1[j] == c) { p = j; break; }
else if (t2[j] == c)
{
Swap(ref t1[j], ref t2[j]);
Swap(ref num1[j], ref num2[j]);
p = j; break;
}
}
}
ans += Math.Min(dp[0, 1], Math.Min(dp[1, 0], dp[1, 1]));
}
return ans;
}
static public T[] Enumerate<T>(int n, Func<int, T> f) { var a = new T[n]; for (int i = 0; i < n; ++i) a[i] = f(i); return a; }
static public void Swap<T>(ref T a, ref T b) { var tmp = a; a = b; b = tmp; }
}
コメント
- フローだと思って考察して最大カットに帰着させてしまった