結果

問題 No.2675 KUMA
ユーザー 👑 hitonanodehitonanode
提出日時 2024-03-15 22:12:22
言語 Python3
(3.12.2 + numpy 1.26.4 + scipy 1.12.0)
結果
AC  
実行時間 1,419 ms / 2,000 ms
コード長 10,714 bytes
コンパイル時間 127 ms
コンパイル使用メモリ 12,928 KB
実行使用メモリ 69,924 KB
最終ジャッジ日時 2024-03-15 22:13:48
合計ジャッジ時間 6,912 ms
ジャッジサーバーID
(参考情報)
judge15 / judge12
外部呼び出し有り
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1,327 ms
69,280 KB
testcase_01 AC 1,327 ms
67,488 KB
testcase_02 AC 1,267 ms
69,024 KB
testcase_03 AC 1,308 ms
69,028 KB
testcase_04 AC 1,338 ms
69,028 KB
testcase_05 AC 1,318 ms
69,412 KB
testcase_06 AC 1,326 ms
69,412 KB
testcase_07 AC 1,330 ms
69,024 KB
testcase_08 AC 1,305 ms
69,668 KB
testcase_09 AC 1,306 ms
68,900 KB
testcase_10 AC 1,333 ms
69,796 KB
testcase_11 AC 1,333 ms
69,668 KB
testcase_12 AC 1,305 ms
69,252 KB
testcase_13 AC 1,282 ms
69,156 KB
testcase_14 AC 1,286 ms
69,540 KB
testcase_15 AC 1,324 ms
69,028 KB
testcase_16 AC 1,345 ms
69,396 KB
testcase_17 AC 1,313 ms
69,028 KB
testcase_18 AC 1,301 ms
69,028 KB
testcase_19 AC 1,288 ms
69,284 KB
testcase_20 AC 1,285 ms
69,028 KB
testcase_21 AC 1,348 ms
69,284 KB
testcase_22 AC 1,326 ms
69,412 KB
testcase_23 AC 1,311 ms
69,156 KB
testcase_24 AC 1,321 ms
69,412 KB
testcase_25 AC 1,334 ms
69,412 KB
testcase_26 AC 1,373 ms
69,156 KB
testcase_27 AC 1,318 ms
69,412 KB
testcase_28 AC 1,357 ms
69,412 KB
testcase_29 AC 1,351 ms
69,156 KB
testcase_30 AC 1,293 ms
69,412 KB
testcase_31 AC 1,289 ms
69,412 KB
testcase_32 AC 1,293 ms
69,412 KB
testcase_33 AC 1,315 ms
69,408 KB
testcase_34 AC 1,317 ms
69,412 KB
testcase_35 AC 1,337 ms
69,412 KB
testcase_36 AC 1,357 ms
69,668 KB
testcase_37 AC 1,336 ms
69,412 KB
testcase_38 AC 1,308 ms
69,792 KB
testcase_39 AC 1,331 ms
69,664 KB
testcase_40 AC 1,363 ms
69,796 KB
testcase_41 AC 1,335 ms
69,668 KB
testcase_42 AC 1,419 ms
69,668 KB
testcase_43 AC 1,354 ms
69,668 KB
testcase_44 AC 1,415 ms
69,408 KB
testcase_45 AC 1,324 ms
69,924 KB
testcase_46 AC 1,362 ms
69,796 KB
testcase_47 AC 1,335 ms
69,536 KB
testcase_48 AC 1,286 ms
69,668 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

from enum import Enum
from typing import SupportsFloat

from scipy.optimize import linprog  # type: ignore


class LPVarType(Enum):
    CONTINUOUS = 1
    INTEGER = 2


class _LPBase:
    def __add__(self, other: "LPExpressionLike") -> "LPExpression":
        return LPExpression.build(self) + other

    def __radd__(self, other: "LPExpressionLike") -> "LPExpression":
        return other + LPExpression.build(self)

    def __sub__(self, other: "LPExpressionLike") -> "LPExpression":
        return LPExpression.build(self) - other

    def __rsub__(self, other: "LPExpressionLike") -> "LPExpression":
        return other - LPExpression.build(self)

    def __mul__(self, other: SupportsFloat) -> "LPExpression":
        return LPExpression.build(self) * other

    def __rmul__(self, other: SupportsFloat) -> "LPExpression":
        return LPExpression.build(self) * other

    def __truediv__(self, other: SupportsFloat) -> "LPExpression":
        return LPExpression.build(self) / other

    def __neg__(self) -> "LPExpression":
        return -LPExpression.build(self)

    def __le__(
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(
            lhs=LPExpression.build(self),
            rhs=other,
            inequality_type=LPInequalityType.LESSEQ,
        )

    def __ge__(
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(
            lhs=other,
            rhs=LPExpression.build(self),
            inequality_type=LPInequalityType.LESSEQ,
        )

    def __eq__(  # type: ignore
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(
            lhs=LPExpression.build(self),
            rhs=other,
            inequality_type=LPInequalityType.EQUAL,
        )


class LPVar(_LPBase):
    def __init__(
        self,
        name: str,
        lower_bound: float | None = None,
        upper_bound: float | None = None,
        variable_type: LPVarType = LPVarType.CONTINUOUS,
    ) -> None:
        self.name = name
        self.lower_bound: float | None = lower_bound
        self.upper_bound: float | None = upper_bound
        self.variable_type: LPVarType = variable_type
        self._value: float | None = None

    def __str__(self) -> str:
        s = "{}(lb={}, ub={}, type={})".format(
            self.name,
            self.lower_bound,
            self.upper_bound,
            self.variable_type.name,
        )
        if self._value is not None:
            s += ": {}".format(self._value)
        return s

    def __repr__(self) -> str:
        return str(self)

    def value(self) -> float | None:
        return self._value


class LPInequalityType(Enum):
    LESSEQ = 1  # lhs <= rhs
    EQUAL = 2  # lhs == rhs


class _LPTerm(_LPBase):
    """
    A term of the form coefficient * variable.
    """

    def __init__(self, coefficient: float, variable: LPVar) -> None:
        self.coefficient: float = coefficient
        self.variable: LPVar = variable


class LPExpression:
    def __init__(
        self,
        const: SupportsFloat,
        terms: list[_LPTerm],
    ) -> None:
        self.const = float(const)
        self.terms = terms.copy()
        self._value: float | None = None

    @classmethod
    def build(self, x: "LPExpressionLike | _LPBase") -> "LPExpression":
        if isinstance(x, LPExpression):
            return LPExpression(x.const, x.terms)
        elif isinstance(x, _LPTerm):
            return LPExpression(0, [x])
        elif isinstance(x, LPVar):
            return LPExpression(0, [_LPTerm(1.0, x)])
        elif isinstance(x, SupportsFloat):
            return LPExpression(x, [])
        else:
            raise TypeError("Invalid type for LPExpression")

    def __add__(self, other: "LPExpressionLike") -> "LPExpression":
        rhs = LPExpression.build(other)
        return LPExpression(
            self.const + rhs.const,
            self.terms + rhs.terms,
        )

    def __radd__(self, other: "LPExpressionLike") -> "LPExpression":
        return self + other

    def __sub__(self, other: "LPExpressionLike") -> "LPExpression":
        rhs = LPExpression.build(other)
        return self + (-rhs)

    def __rsub__(self, other: "LPExpressionLike") -> "LPExpression":
        return LPExpression.build(other) + (-self)

    def __mul__(self, other: SupportsFloat) -> "LPExpression":
        return LPExpression(
            self.const * float(other),
            [
                _LPTerm(
                    t.coefficient * float(other),
                    t.variable,
                )
                for t in self.terms
            ],
        )

    def __rmul__(self, other: SupportsFloat) -> "LPExpression":
        return self * other

    def __truediv__(self, other: SupportsFloat) -> "LPExpression":
        return LPExpression(
            self.const / float(other),
            [
                _LPTerm(
                    t.coefficient / float(other),
                    t.variable,
                )
                for t in self.terms
            ],
        )

    def __neg__(self) -> "LPExpression":
        return LPExpression(
            -self.const,
            [_LPTerm(-t.coefficient, t.variable) for t in self.terms],
        )

    def __le__(
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(self, other, LPInequalityType.LESSEQ)

    def __ge__(
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(other, self, LPInequalityType.LESSEQ)

    def __eq__(  # type: ignore
        self,
        other: "LPExpressionLike",
    ) -> "LPInequality":
        return LPInequality(self, other, LPInequalityType.EQUAL)

    def __str__(self) -> str:
        ret = ["{}".format(self.const)]

        for t in self.terms:
            sgn = "+" if t.coefficient >= 0 else ""
            ret.append("{}{}{}".format(sgn, t.coefficient, t.variable.name))

        return " ".join(ret)

    def value(self) -> float | None:
        return self._value


LPExpressionLike = LPExpression | _LPTerm | LPVar | SupportsFloat


class LPInequality:
    def __init__(
        self,
        lhs: LPExpressionLike,
        rhs: LPExpressionLike,
        inequality_type: LPInequalityType,
    ) -> None:
        """
        lhs <= rhs
        -> terms + const (<= or ==) 0
        """

        self.lhs = LPExpression.build(lhs) - LPExpression.build(rhs)
        self.inequality_type = inequality_type

    def __str__(self) -> str:
        if self.inequality_type == LPInequalityType.LESSEQ:
            return "{} <= 0".format(self.lhs)
        elif self.inequality_type == LPInequalityType.EQUAL:
            return "{} == 0".format(self.lhs)
        else:
            raise ValueError("Invalid inequality type")

    def __repr__(self) -> str:
        return str(self)


class LPModel:
    def __init__(self) -> None:
        self.constraints: list[LPInequality] = []
        self.objective: LPExpression = LPExpression(0, [])

    def add_constraint(self, constraint: LPInequality) -> None:
        self.constraints.append(constraint)

    def set_objective(self, objective: LPExpressionLike) -> None:
        self.objective = LPExpression.build(objective)

    def solve(self) -> bool:
        var_dict: dict[int, LPVar] = {}
        for constraint in self.constraints:
            for term in constraint.lhs.terms:
                var_dict.setdefault(id(term.variable), term.variable)

        for term in self.objective.terms:
            var_dict.setdefault(id(term.variable), term.variable)

        id_to_idx = {id(v): i for i, v in enumerate(var_dict.values())}

        A_ub: list[list[float]] = []
        b_ub: list[float] = []
        A_eq: list[list[float]] = []
        b_eq: list[float] = []

        for constraint in self.constraints:
            lhs: list[float] = [0.0] * len(var_dict)
            rhs = -constraint.lhs.const

            for term in constraint.lhs.terms:
                lhs[id_to_idx[id(term.variable)]] += term.coefficient

            if constraint.inequality_type == LPInequalityType.LESSEQ:
                A_ub.append(lhs)
                b_ub.append(rhs)
            elif constraint.inequality_type == LPInequalityType.EQUAL:
                A_eq.append(lhs)
                b_eq.append(rhs)
            else:
                raise ValueError("Invalid inequality type")

        bounds = [(v.lower_bound, v.upper_bound) for v in var_dict.values()]

        integrality = [
            int(variable.variable_type == LPVarType.INTEGER)
            for variable in var_dict.values()
        ]

        c: list[float] = [0.0] * len(var_dict)
        for term in self.objective.terms:
            c[id_to_idx[id(term.variable)]] += term.coefficient

        res = linprog(
            c,
            A_ub=A_ub or None,
            b_ub=b_ub or None,
            A_eq=A_eq or None,
            b_eq=b_eq or None,
            bounds=bounds,
            integrality=integrality,
        )

        if res.status == 0:
            for i, variable in enumerate(var_dict.values()):
                variable._value = res.x[i]

            self.objective._value = res.fun + self.objective.const

            return True
        else:
            return False


from itertools import product

N = int(input())
xys = [tuple(map(int, input().split())) for _ in range(N)]

keimas = [
    (2, -1),
    (2, 1),
    (-2, -1),
    (-2, 1),
    (1, 2),
    (1, -2),
    (-1, 2),
    (-1, -2),
]

targets = []

for x, y in xys:
    for a, b in keimas:
        targets.append((x + a, y + b))

targets = [x for x in sorted(set(targets)) if x not in xys]

# print(targets)

lp = LPModel()

dirs = [
    [(2, -1), (2, 1)],
    [(-2, -1), (-2, 1)],
    [(-1, -2), (1, -2)],
    [(-1, 2), (1, 2)],
]

sgns = [LPVar(f'{i}{j}', lower_bound=0, upper_bound=1, variable_type=LPVarType.INTEGER) for i, j in product(range(len(targets)), range(len(dirs)))]

for j in range(N):
    vs = []
    for i, (x, y) in enumerate(targets):
        for d, dir in enumerate(dirs):
            match = False
            for dx, dy in dir:
                if (x + dx, y + dy) == xys[j]:
                    match = True
                    break
            
            if match:
                vs.append(sgns[i * len(dirs) + d])

    if vs:
        lp.add_constraint(sum(vs) >= 1)
    else:
        print(-1)
        exit()

for i in range(len(targets)):
    lp.add_constraint(sum([sgns[i * len(dirs) + d] for d in range(len(dirs))]) <= 1)

lp.set_objective(sum(sgns))

if not lp.solve():
    print(-1)
    exit()

print(int(lp.objective.value() + 0.1))
0