トピックのソース
2287 -- Tian Ji -- 競馬 (poj.org)
トピックの説明
天吉の競馬は中国の歴史の中で有名な話です。
この物語は 2300 年前に起こりました。Tian Ji は斉国の高官で、斉王や他の王子たちと競馬をするのが好きでした。
田記も斉王も下級馬、中級馬、上級馬の3種類の馬を持っています。
ラウンドは全部で 3 ラウンドあり、各馬は 1 ラウンドのみ使用できます。各ラウンドの勝者は敗者から 200 枚の銀貨を獲得できます。
斉王は斉で最も権力のある人物であるため、彼の馬は非常に優れており、どのレベルの馬も同じレベルの田吉の馬よりも優れています。したがって、田記は斉王に毎回銀貨600枚を失うことになります。
Tian Ji は、中国史上最も有名な軍事顧問の 1 人である Sun Bin に出会うまで、このことに非常に悩みました。Sun Bin は Tian Ji に次の戦略を提供し、Tian Ji が次のゲームで 200 枚の銀貨を取り戻すことができるようにしました。これは非常に単純な戦略です。
- Tian Ji の下級馬 vs. 君主の高級馬 (Tian Ji が負けて銀貨 200 枚を失う)
- Tian Ji の中馬 vs 君主の下馬 (Tian Ji が勝ち、銀貨 200 枚を獲得)
- Tian Ji の高級馬 vs. 君主の中型馬 (Tian Ji が勝ち、銀貨 200 枚を獲得)
実際、上記の競馬の問題は、単純に、2 部グラフで最大の一致を見つけることとみなすことができます。
片面に田吉の馬、もう片面に斉王の馬を描きます。
Tian Ji の馬が Qi Wang の馬に勝つことができる限り、2 頭の馬の間に接続線を引き、2 頭の馬が一致することを期待していることを示します。
次に、Tian Ji ができるだけ多くのラウンドに勝ちます。実際には、このグラフで最大の一致を見つけることです。
同点の場合、問題はさらに複雑になります。考えられるすべてのバリアントに重み 0、1、または -1 を割り当て、最大の重み付き完全一致を見つける必要があります。
次に、競馬問題は非常に特殊な 2 部グラフ マッチング問題です。このグラフは馬の速度によって決まります。高速な頂点は、常に低速な頂点よりもパフォーマンスが優れています。この場合、重み付き 2 部グラフ マッチング アルゴリズムは少しやりすぎのように思えます。
この特殊なマッチング問題を解決するアルゴリズムを設計してください。
説明を入力してください
最大 50 セットのユースケースを入力します。
各ユースケースのセット:
- 最初の行は、各側の馬の数を示す整数 n (n ≤ 100) です。
- 2 行目は n の整数で、Tian Ji の馬の速度を示します。
- 3 行目は n の整数で、斉王の馬の速度を示しています。
入力の終了条件は、最後のケースのセットの後に「0」のみの行を入力することです。
出力の説明
ユースケースのセットごとに 1 行を出力します。各行には、このユースケースのセットで Tian Ji が取得できる銀貨の最大数を示す整数が含まれます。
例
入力 | 3 92 83 71 95 87 74 2 20 20 20 20 2 20 19 22 18 0 |
出力 | 200 0 0 |
説明する | なし |
トピック分析
この問題は貪欲な思考を使えば解決できます。
つまり、Tian Ji が最も多くの銀貨を手に入れたい場合、Tian Ji の損失を最小限に抑える必要があります。では、Tian Ji の損失を最小限にするにはどうすればよいでしょうか。
それは、田記の「負けなければならない」馬と「最も遅い」馬を利用して、斉王の最速の馬を消費することです。
したがって、最初に Tian Ji と King Qi の馬を昇順に並べ替えて、最も遅い馬と最も速い馬を見つける必要があります。
- Tian Ji の最速の馬 > King Qi の最速の馬の場合、Tian Ji がこのラウンドで勝ちます
- Tian Ji の最速の馬 < King Qi の最速の馬の場合、Tian Ji はこのラウンドで負けますが、Tian Ji の最速の馬を維持するには、Tian Ji にレースで最も遅い馬を倒させて、レースが次のラウンドになるようにする必要があります。最小限のコスト損失。
- 最速の馬 == 斉王の最速の馬の条件の場合、田吉はこのラウンドを引きますが、その代償は確かに失われます。この時点では、田吉の馬の中から負けなければならない馬と、最も遅い馬を見つけることを検討する必要があります。 King Qi の最速の馬を消費します。このラウンドで負けましたが、Tian Ji は事前に失う必要がある馬を失っただけです。利点は、最速の馬を維持したことです。そこで次は、Tian Ji が負けるであろう最も遅い馬を見つける必要があります。
- Tian Ji の最も遅い馬 > King Qi の最も遅い馬である場合、現在の Tian Ji の最も遅い馬は Tian Ji が失うであろう最も遅い馬ではなく、引き続き探し続ける必要があります。
- 田記の最も遅い馬 < 斉王の最も遅い馬の場合、現在の田記の最も遅い馬は田記が失うであろう最も遅い馬であり、我々は斉王の最も速い馬と競争する必要があります。
- Tian Ji の最も遅い馬 == King Qi の最も遅い馬である場合、現在の Tian Ji の最も遅い馬は、Tian Ji が失うべき最も遅い馬ではないため、引き続き検索する必要があります。
JSアルゴリズムのソースコード
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
rl.on("line", (line) => {
if (line == "0") {
const cases = [];
for (let i = 0; i < lines.length; i += 3) {
const n = parseInt(lines[i]);
const a = lines[i + 1].split(" ").map(Number); // 田忌的马速度数组
const b = lines[i + 2].split(" ").map(Number); // 齐王的马速度数组
cases.push([n, a, b]);
}
getResult(cases);
lines.length = 0;
} else {
lines.push(line);
}
});
function getResult(cases) {
for (let c of cases) {
const n = c[0];
const a = c[1];
const b = c[2];
a.sort((a, b) => a - b);
b.sort((a, b) => a - b);
let la = 0; // 指向田忌最慢的马
let ra = n - 1; // 指向田忌最快的马
let lb = 0; // 指向齐王最慢的马
let rb = n - 1; // 指向齐王最快的马
let ans = 0; // 记录田忌获得银币数
while (la <= ra) {
if (a[ra] > b[rb]) {
// 田忌最快的马 比 齐王最快的马要快, 则直接比
ans += 200;
ra--;
rb--;
} else if (a[ra] < b[rb]) {
// 田忌最快的马 比 齐王最快的马要慢, 则结果肯定输, 为了保留田忌最快的马, 我们应该用田忌最慢的马去消耗掉齐王最快的马
ans -= 200;
la++;
rb--;
} else {
// 田忌最快的马 和 齐王最快的 速度相同, 此时如果平局的话,则会让田忌损失最快的马,因此我们应该找到田忌最慢的马, 即田忌必输的马来消耗掉齐王最快的马
if (a[la] > b[lb]) {
// 如果田忌最慢的马 比 齐王最慢的马 快, 则此时田忌最慢的马不是必输的马
ans += 200;
la++;
lb++;
} else {
// 如果田忌最慢的马速度 <= 齐王最慢的马速度, 此时应该让田忌最慢的马 去消耗 齐王最快的马
// 如果齐王最快的马速度 > 田忌最慢的马速度,则田忌失去银币
// 如果齐王最快的马速度 == 田忌最慢的马速度,则田忌不失去银币
if (b[rb] > a[la]) ans -= 200;
la++;
rb--;
}
}
}
console.log(ans);
}
}
Javaアルゴリズムのソースコード
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static class Case {
int n;
int[] a; // 田忌的马速度数组
int[] b; // 齐王的马速度数组
public Case(int n, int[] a, int[] b) {
this.n = n;
this.a = a;
this.b = b;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ArrayList<Case> cases = new ArrayList<>();
// POJ Java只支持jdk1.5, 因此如果需要在POJ验证的话,需要替换为下面更低级的语法
// ArrayList cases = new ArrayList();
while (true) {
String line = sc.next();
if ("0".equals(line)) {
getResult(cases);
break;
} else {
int n = Integer.parseInt(line);
int[] a = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] b = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// POJ Java只支持jdk1.5, 因此如果需要在POJ验证的话,需要替换为下面更低级的语法
// int[] a = new int[n];
// for (int i = 0; i < n; i++) a[i] = Integer.parseInt(sc.next());
//
// int[] b = new int[n];
// for (int i = 0; i < n; i++) b[i] = Integer.parseInt(sc.next());
cases.add(new Case(n, a, b));
}
}
}
public static void getResult(ArrayList<Case> cases) {
for (Case c : cases) {
int n = c.n;
int[] a = c.a;
int[] b = c.b;
Arrays.sort(a);
Arrays.sort(b);
int la = 0; // 指向田忌最慢的马
int ra = n - 1; // 指向田忌最快的马
int lb = 0; // 指向齐王最慢的马
int rb = n - 1; // 指向齐王最快的马
int ans = 0; // 记录田忌获得银币数
while (la <= ra) {
if (a[ra] > b[rb]) {
// 田忌最快的马 比 齐王最快的马要快, 则直接比
ans += 200;
ra--;
rb--;
} else if (a[ra] < b[rb]) {
// 田忌最快的马 比 齐王最快的马要慢, 则结果肯定输, 为了保留田忌最快的马, 我们应该用田忌最慢的马去消耗掉齐王最快的马
ans -= 200;
la++;
rb--;
} else {
// 田忌最快的马 和 齐王最快的 速度相同, 此时如果平局的话,则会让田忌损失最快的马,因此我们应该找到田忌最慢的马, 即田忌必输的马来消耗掉齐王最快的马
if (a[la] > b[lb]) {
// 如果田忌最慢的马 比 齐王最慢的马 快, 则此时田忌最慢的马不是必输的马
ans += 200;
la++;
lb++;
} else {
// 如果田忌最慢的马速度 <= 齐王最慢的马速度, 此时应该让田忌最慢的马 去消耗 齐王最快的马
// 如果齐王最快的马速度 > 田忌最慢的马速度,则田忌失去银币
// 如果齐王最快的马速度 == 田忌最慢的马速度,则田忌不失去银币
if (b[rb] > a[la]) ans -= 200;
la++;
rb--;
}
}
}
System.out.println(ans);
}
}
}
Pythonアルゴリズムのソースコード
# 算法入口
def getResult(cases):
for case in cases:
n = case[0]
a = case[1]
b = case[2]
a.sort()
b.sort()
la = 0 # 指向田忌最慢的马
ra = n - 1 # 指向田忌最快的马
lb = 0 # 指向齐王最慢的马
rb = n - 1 # 指向齐王最快的马
ans = 0 # 记录田忌获得银币数
while la <= ra:
if a[ra] > b[rb]:
# 田忌最快的马 比 齐王最快的马要快, 则直接比
ans += 200
ra -= 1
rb -= 1
elif a[ra] < b[rb]:
# 田忌最快的马 比 齐王最快的马要慢, 则结果肯定输, 为了保留田忌最快的马, 我们应该用田忌最慢的马去消耗掉齐王最快的马
ans -= 200
la += 1
rb -= 1
else:
# 田忌最快的马 和 齐王最快的 速度相同, 此时如果平局的话,则会让田忌损失最快的马,因此我们应该找到田忌最慢的马, 即田忌必输的马来消耗掉齐王最快的马
if a[la] > b[lb]:
# 如果田忌最慢的马 比 齐王最慢的马 快, 则此时田忌最慢的马不是必输的马
ans += 200
la += 1
lb += 1
else:
# 如果田忌最慢的马速度 <= 齐王最慢的马速度, 此时应该让田忌最慢的马 去消耗 齐王最快的马
# 如果齐王最快的马速度 > 田忌最慢的马速度,则田忌失去银币
# 如果齐王最快的马速度 == 田忌最慢的马速度,则田忌不失去银币
if b[rb] > a[la]:
ans -= 200
la += 1
rb -= 1
print(ans)
# 输入获取
cases = []
while True:
line = input()
if line == "0":
# 算法调用
getResult(cases)
break
else:
n = int(line)
a = list(map(int, input().split())) # 田忌的马速度数组
b = list(map(int, input().split())) # 齐王的马速度数组
cases.append([n, a, b])