結果

問題 No.2478 Disjoint-Sparse-Table Optimization
ユーザー 👑 rin204rin204
提出日時 2024-10-14 01:37:47
言語 Python3
(3.12.2 + numpy 1.26.4 + scipy 1.12.0)
結果
AC  
実行時間 1,713 ms / 2,000 ms
コード長 11,802 bytes
コンパイル時間 111 ms
コンパイル使用メモリ 13,824 KB
実行使用メモリ 95,676 KB
最終ジャッジ日時 2024-10-14 01:38:10
合計ジャッジ時間 6,546 ms
ジャッジサーバーID
(参考情報)
judge5 / judge4
外部呼び出し有り
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1,294 ms
69,828 KB
testcase_01 AC 1,285 ms
69,960 KB
testcase_02 AC 1,344 ms
68,412 KB
testcase_03 AC 1,577 ms
80,548 KB
testcase_04 AC 1,540 ms
79,756 KB
testcase_05 AC 1,392 ms
71,948 KB
testcase_06 AC 1,427 ms
75,524 KB
testcase_07 AC 1,527 ms
76,828 KB
testcase_08 AC 1,416 ms
76,724 KB
testcase_09 AC 1,555 ms
77,836 KB
testcase_10 AC 1,420 ms
78,500 KB
testcase_11 AC 1,705 ms
82,292 KB
testcase_12 AC 1,350 ms
68,028 KB
testcase_13 AC 1,713 ms
95,676 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

# https://github.com/hitonanode/linprog-modeler/blob/main/lpmodeler/model.py

from enum import Enum
from typing import SupportsFloat

from scipy.optimize import linprog  # type: ignore


class LPVarType(Enum):
    CONTINUOUS = 0
    INTEGER = 1
    SEMICONTINUOUS = 2  # 0 or lb <= x <= ub
    SEMIINTEGER = 3  # 0 or lb <= x <= ub, integer


class LPStatus(Enum):
    OPTIMAL = 0
    ITERATION_LIMIT = 1
    INFEASIBLE = 2
    UNBOUNDED = 3
    NUMERICAL_ERROR = 4


class LPSense(Enum):
    MINIMIZE = 0
    MAXIMIZE = 1


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, sense: LPSense = LPSense.MINIMIZE) -> None:
        self.sense = sense
        self.has_impossible_constraints = False
        self.constraints: list[LPInequality] = []
        self.objective: LPExpression = LPExpression(0, [])
        self.status: LPStatus | None = None

    def add_constraint(self, constraint: LPInequality | bool) -> None:
        if isinstance(constraint, bool):
            if not constraint:
                self.has_impossible_constraints = True
        else:
            self.constraints.append(constraint)

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

    def solve(self) -> None:
        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)

        # Reset status
        self.objective._value = None
        self.status = None
        for var in var_dict.values():
            var._value = None

        # Obviously infeasible
        if self.has_impossible_constraints:
            self.status = LPStatus.INFEASIBLE
            return

        # Obviously optimal
        if not var_dict:
            self.status = LPStatus.OPTIMAL
            self.objective._value = self.objective.const
            return

        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 = [v.variable_type.value for v in var_dict.values()]

        c: list[float] = [0.0] * len(var_dict)

        func_weight = 1 if self.sense == LPSense.MINIMIZE else -1

        for term in self.objective.terms:
            c[id_to_idx[id(term.variable)]] += term.coefficient * func_weight

        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 sum(integrality) else None,
        )

        if res.status == 0:
            self.status = LPStatus.OPTIMAL

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

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

        elif res.status == 1:
            self.status = LPStatus.ITERATION_LIMIT

        elif res.status == 2:
            self.status = LPStatus.INFEASIBLE

        elif res.status == 3:
            self.status = LPStatus.UNBOUNDED

        elif res.status == 4:
            self.status = LPStatus.NUMERICAL_ERROR

        else:
            raise ValueError("Invalid status code")


Q = int(input())
LR = [list(map(int, input().split())) for _ in range(Q)]
A = list(map(int, input().split()))
cum = [0]
for a in A:
    cum.append(cum[-1] + a)

edges = []
ans = 0

for i in range(Q):
    l1, r1 = LR[i]
    ans += cum[r1 - 1] - cum[l1 - 1]
    for j in range(Q):
        l2, r2 = LR[j]
        if l1 < l2 and l2 < r1 and r1 < r2:
            edges.append((i, j, cum[r1 - 1] - cum[l2 - 1]))

model = LPModel(LPSense.MAXIMIZE)
edge_vars = [LPVar(f"edge_{i}", 0, 1, LPVarType.INTEGER) for i in range(len(edges))]

E = [[] for _ in range(Q)]
for i, (u, v, _) in enumerate(edges):
    E[u].append(edge_vars[i])
    E[v].append(edge_vars[i])

for i in range(Q):
    model.add_constraint(sum(E[i]) <= 1)

model.set_objective(sum(w * e for (_, _, w), e in zip(edges, edge_vars)))
model.solve()

assert model.status == LPStatus.OPTIMAL
ans -= round(model.objective.value())
print(ans)
0