【Flet v0.26.0】Fletで作ったアプリケーションをGithub Actionsによって自動でビルド & Releaseとして公開したい
はじめに
以前、 Python でマルチプラットフォームで GUI アプリケーションを作ることができるフレームワーク「Flet」について紹介した。
その際、アプリケーションのビルドに関しては手元の Python 仮想環境でコマンドを打って行っていた。
今回はこの作業を Github Actions で自動化したいと思う。
想定する作業の流れ
- VScode 上でアプリケーションのコードを書く。
- VScode から Git 拡張機能等を利用してローカルに commit & リモートに push
- リリース段階に至ったら、該当する commit に Tag を作成する。
- tag の作成をトリガーとして、 Github Actions が実行される。この Actions では、以下の処理が行われる。
- Release を draft 状態で作成する。
- 任意のプラットフォームでビルドを行う。
- 作成された実行可能ファイル等(これらは
build内に格納されている)をまとめて圧縮する。 - 圧縮されたファイル群を Release に添付する。
- draft 状態の Release が問題なければ手動で公開する。
Workflow の作成
以下の記事を参考に、Github Actions のWorkflow を書いていく。 Workflow の名前は build-flet-cross.ymlとした。
ただし、 Flet v0.26.0 ではビルド時のコマンドが一部変更されているので、そのまま用いることはできない。
そこで、以下のリポジトリも参照する。
Workflow のトリガーと環境変数の作成
name: build-flet-cross on: push: tags: - "v*" workflow_dispatch: env: BUILD_NUMBER: 1 BUILD_VERSION: 0.1.0 PYTHON_VERSION: 3.13.1 FLUTTER_VERSION: 3.24.3
on以下で、この Workflow が実行されるトリガーを設定している。今回は、
push:>tags:>- "v*": 「v」から始まる名前の Tag を作成したときに実行するworkflow_dispatch: Github の Actions タブから手動で Workflow を実行できるようにする
の2つのトリガーを設定した。
また、環境変数として次の4つを作成した。
BUILD_NUMBER: 内部バージョン番号として使用される識別子。各ビルドには、以前のビルドと区別するための一意の識別子が必要で、数字が大きいほど新しいビルドであることを示す。BUILD_VERSION: ユーザーに表示されるバージョン番号。PYTHON_VERSION: 使用する Python のバージョン。FLUTTER_VERSION: ビルドにおいて使用する Flutter SDK のバージョン。
Release の作成
jobs: create-release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: "Create Release" run: gh release create ${{github.ref_name}} --draft --verify-tag --notes "Release ${{github.ref_name}}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Github CLI を使って、コマンドラインから Release を draft 状態で作成する。
マルチプラットフォームビルド
jobs: cross-build: needs: create-release strategy: matrix: include: - runs-on: macos-latest binary-name: ${{ vars.APP_NAME }}_macos.tar.gz target-platform: macos - runs-on: windows-latest binary-name: ${{ vars.APP_NAME }}_windows.zip target-platform: windows runs-on: ${{ matrix.runs-on }} permissions: contents: write
matrix strategy を利用して、複数のプラットフォームでのビルドを同時並行で処理している。今回は Windows と Mac OS でのビルドを行っている。
vars.APP_NAMEには、アプリの名前を環境変数にして代入している。これは、リポジトリの「Settings」から「Security」タブの「Secrets and variables」>「Actions」から設定可能。
Python と Flutter SDK のインストール
jobs: cross-build: steps: - name: Checkout Codes uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set environment variable for UTF-8 run: echo "PYTHONUTF8=1" >> $env:GITHUB_ENV - name: Install Python Dependencies run: | python -m pip install --upgrade pip pip install . - name: Setup Flutter uses: subosito/flutter-action@v2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: 'stable'
PYTHON_VERSIONで指定した Python バージョンを、 actions/setup-pythonを利用してインストールする。
また、
echo "PYTHONUTF8=1" >> $env:GITHUB_ENV
で、文字コードを UTF-8 に設定する。これをしないと、 Windows での Flet インストール時にエラーが出る。
そして、pyproject.tomlを利用して Python の依存パッケージをインストールした後、actions/flutter-actionで Flutter SDK をインストールする。
Windows でのビルド
jobs: cross-build: steps: - name: Windows Build if: runner.os == 'Windows' run: | chcp 65001 flutter config --no-analytics flet build windows --verbose --no-rich-output --build-number=${{ env.BUILD_NUMBER }} --build-version=${{ env.BUILD_VERSION }} Compress-Archive -Path ./build/windows/* -DestinationPath ./build/windows/${{ matrix.binary-name }} - name: Upload Windows Artifact if: runner.os == 'Windows' uses: actions/upload-artifact@v4.3.4 with: name: windows-build-artifact path: build/windows if-no-files-found: error overwrite: false
flet build windowsでパッケージ化した実行ファイル群は、build/windows以下に生成される。これを zip ファイル化し、 Artifact としてアップロードしている。
buildコマンドのオプションについては公式ドキュメントを参照。
Mac OS でのビルド
jobs: cross-build: steps: - name: MacOS Build if: runner.os == 'macos' run: | flutter config --no-analytics flet build macos --verbose --build-number=${{ env.BUILD_NUMBER }} --build-version=${{ env.BUILD_VERSION }} tar -zcvf ./build/macos/${{ matrix.binary-name }} -C ./build/macos . - name: Upload MacOS Artifact if: runner.os == 'macos' uses: actions/upload-artifact@v4.3.4 with: name: macos-build-artifact path: build/macos if-no-files-found: error overwrite: false
やっていることは Windows の場合と同じ。
Release へのアップロード
jobs: cross-build: steps: - name: Upload Release if: runner.os == 'Windows' run: gh release upload ${{ github.ref_name }} ./build/windows/${{ matrix.binary-name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Release if: runner.os == 'macos' run: gh release upload ${{ github.ref_name }} ./build/macos/${{ matrix.binary-name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
先ほど Artifact としてアップロードした実行ファイル群を、最初に作成した Release にアタッチする。
ここまでの内容を1つのまとめると以下のようになる。
name: build-flet-cross on: push: tags: - "v*" workflow_dispatch: env: BUILD_NUMBER: 1 BUILD_VERSION: 0.1.0 PYTHON_VERSION: 3.13.1 FLUTTER_VERSION: 3.24.3 jobs: create-release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: "Create Release" run: gh release create ${{github.ref_name}} --draft --verify-tag --notes "Release ${{github.ref_name}}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} cross-build: needs: create-release strategy: matrix: include: # - runs-on: ubuntu-latest # binary-name: ${{ vars.APP_NAME }}_linux.tar.gz # target-platform: linux - runs-on: macos-latest binary-name: ${{ vars.APP_NAME }}_macos.tar.gz target-platform: macos - runs-on: windows-latest binary-name: ${{ vars.APP_NAME }}_windows.zip target-platform: windows runs-on: ${{ matrix.runs-on }} permissions: contents: write steps: - name: Checkout Codes uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set environment variable for UTF-8 run: echo "PYTHONUTF8=1" >> $env:GITHUB_ENV - name: Install Python Dependencies run: | python -m pip install --upgrade pip pip install . - name: Setup Flutter uses: subosito/flutter-action@v2 with: flutter-version: ${{ env.FLUTTER_VERSION }} channel: 'stable' - name: Windows Build if: runner.os == 'Windows' run: | chcp 65001 flutter config --no-analytics flet build windows --verbose --no-rich-output --build-number=${{ env.BUILD_NUMBER }} --build-version=${{ env.BUILD_VERSION }} Compress-Archive -Path ./build/windows/* -DestinationPath ./build/windows/${{ matrix.binary-name }} - name: Upload Windows Artifact if: runner.os == 'Windows' uses: actions/upload-artifact@v4.3.4 with: name: windows-build-artifact path: build/windows if-no-files-found: error overwrite: false - name: MacOS Build if: runner.os == 'macos' run: | flutter config --no-analytics flet build macos --verbose --build-number=${{ env.BUILD_NUMBER }} --build-version=${{ env.BUILD_VERSION }} tar -zcvf ./build/macos/${{ matrix.binary-name }} -C ./build/macos . - name: Upload MacOS Artifact if: runner.os == 'macos' uses: actions/upload-artifact@v4.3.4 with: name: macos-build-artifact path: build/macos if-no-files-found: error overwrite: false # - name: Patch for linux build # run: | # flutter doctor # sudo apt-get update -y # sudo apt-get install -y ninja-build libgtk-3-dev # flutter doctor # - name: Flet Build Linux # run: | # flutter config --no-analytics # flet build linux --verbose --build-number=${{ env.BUILD_NUMBER }} --build-version=${{ env.BUILD_VERSION }} # tar --warning=no-file-changed -zcvf ./build/linux/${{ matrix.binary-name }} -C ./build/linux . # - name: Upload Linux Artifact # uses: actions/upload-artifact@v4.3.4 # with: # name: linux-build-artifact # path: build/linux # if-no-files-found: error # overwrite: false - name: Upload Release if: runner.os == 'Windows' run: gh release upload ${{ github.ref_name }} ./build/windows/${{ matrix.binary-name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Release if: runner.os == 'macos' run: gh release upload ${{ github.ref_name }} ./build/macos/${{ matrix.binary-name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # - name: Upload Release # if: runner.os == 'Linux' # run: gh release upload ${{ github.ref_name }} ./build/linux/${{ matrix.binary-name }} # env: # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
※ Linux でのビルドも一応書いたのだが、うまく動かなかったのでコメントアウトしている。
Release の公開
Actions が正常に終了したら、 draft 状態の Release を公開する。

終わりに
Github Actions を利用することで、ビルド作業で手を止めることなく開発に集中できるようになった。
【Flet v0.26.0】Python + FletでリッチなGUIアプリケーションを手軽に作ってみる ~tkinterはもう古い!?~
はじめに
突然だが、
Python の豊富なライブラリ群を生かして GUI アプリケーションを作りたい!
こう思ったことはないだろうか。
Python には、古くから tkinter という GUI ツールキットが標準ライブラリとして搭載されてきた。
しかし、使ってみたことがある人ならわかるだろうが、 tkinter の GUI のデザインはあまりにもダサすぎると私は思うのだ。
どうせ自作で作るなら、 Google や Apple のアプリでつかわれているようなリッチなデザインの GUI を使いたい。
そんな欲望を満たしてくれるフレームワークがこの世には存在する。その名も Flet である。
ということで本記事では、 Python のフレームワーク Flet に関する紹介と、開発環境構築からアプリケーションのビルドまでの流れを書くことにする。
なお、 Flet の詳しい機能や細かい処理の流れなどについては、この記事では詳しく扱わない。幸いなことに公式ドキュメントがある程度充実しているので、そちらを参照のこと。
また、開発環境構築に関連する内容は Windows を対象としているので悪しからず。
Flet とは何か?
Flet は、フロントエンドの知識なしに、 Python のみで Web やデスクトップ、モバイルといった様々なプラットフォームのアプリケーションを作成できるフレームワークである。
UI は Google が開発する Flutter をベースにした「Control」と呼ばれるコンポーネントを使用して構築される。 HTML や CSS 、 Javascript といった他の言語の知識は一切必要ない。
主要なライブラリパッケージはfletのみで、 Python 環境上でpipコマンドを一発打つだけですぐに開発に移ることができる。
ただ、良い面もあれば悪い面もあるわけで。
まず、 Python という言語の特性上、大規模・複数人での開発に向いていないという点がある。また、実行速度もそれほど早いわけではないので、大量のデータをリアルタイムに処理したり、複雑な UI を組み合わせたりするようなアプリケーションの作成には向いていない。
さらに、フレームワークのバージョンがまだ0.xであり、正式版ではない。そのため、将来のバージョンで破壊的な変更が加えられる可能性がある。
これらのメリット・デメリットを見てみると、個人で小規模な作業時短ツールを Python でサクッと作りたいというときに採用するのが一番良さそうだ。
Flet の開発環境構築
ここからは、実際に Flet を触っていこう。
...とその前に、開発環境の構築をしていく。
Python 仮想環境の作成
今回は Python 自体のバージョン管理に pyenv を、ライブラリのバージョンの管理に venv を用いることにする。
基本的には以下の記事に沿って進めていくが、 venv 自体は Python にデフォルトで備わっているので、インストールするのは実質 pyenv のみ。
pyenv-win のインストール
早速 pyenv をインストール... と言いたいところだが、 pyenv は Windows には対応していないため、代わりに pyenv-win を用いる必要がある。
- Windows PowerShell を管理者権限で開き、次のコマンドを実行。
> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
「実行ポリシーの変更」について確認がなされるが、[Y] はい(Y)でよい。
- Windows PowerShell を再起動したら、次のコマンドで pyenv-win をインストール。
> Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"
以下のコマンドでインストール可能な Python バージョンが列挙されたら、 pyenv のインストールは成功。
> pyenv install --list
次に、 Python をインストールしていこう。 Flet v0.26.0 では Python 3.9 以上での動作を保証しているようなので、とりあえず現時点での最新バージョンを入れておけば問題ないだろう。この記事の執筆時点では 3.13.1 が最新であった。
- 次のコマンドを実行。
> pyenv install 3.13.1
:: [Info] :: completed! 3.13.1と表示されたら成功。
> pyenv versions
を実行すると、インストール済みの Python のバージョンが一覧で表示される。
venv のインストール
続いて、 venv のインストールを行う。今回は、 Flet の公式ドキュメントに沿って、プロジェクトディレクトリの配下に.venvディレクトリを作成することにする。
- まず、お好みの場所にプロジェクトディレクトリを作り(今回は
flet-testとした)、その中に移動する。そして、以下のコマンドを実行する。
> python -m venv .venv
特に実行結果が表示されることはない。
- 次のコマンドで仮想環境を起動させる。
> .venv\Scripts\activate
(.venv) >のような形で入力待ちになれば成功。以降のコマンドは全て仮想環境内で実行する。
- このプロジェクトで使用する Python のバージョンを次のコマンドで指定する。
> pyenv local 3.13.1
次のコマンドを実行して、指定したバージョンが返ってくれば成功。
> pyenv version
Flet のインストール
さて、いよいよ本命の Flet をインストールしていく。
- 次のコマンドを実行する。
> pip install flet[all]
以下のコマンドでバージョン情報が返ってくれば成功。
> flet --version
...これで Flet の開発環境の構築は完了。簡単すぎて拍子抜けしてしまった。
簡単なアプリケーションを作ってみる
環境構築が完了したので、簡単な Flet アプリケーションを作ってみよう。
- 以下のコマンドで、Flet アプリケーションの雛形を作成する。
> flet create
ディレクトリ構成は以下のようになるはず。
flet-test ├ .venv (中身は省略) ├ src │ ├ assets │ │ └ icon.png │ └ main.py ├ .gitignore ├ .python-version ├ pyproject.toml └ README.md
ここからmain.pyを書き換えてアプリケーションを作っていくわけだが、実はこの時点で動くものはできている。
- 以下のコマンドでアプリケーションを実行。
> flet run
Python スクリプトの名前はmain.pyでなくてもよいが、その場合は flet run <スクリプト名>で実行する必要がある。

正直これだけだと味気ないが、公式ドキュメントには様々なチュートリアルが掲載されている。
色々な Control が用意されているので、 Python のライブラリと組み合わせれば、作ることができるアプリの幅はかなり広いと言えるだろう。
アプリケーションのビルド
先述した Flet の強みとして、「クロスプラットフォーム対応」というものがあった。
ということで、先ほど作ったカウンターアプリを配布すると仮定して、ビルド作業をしてみよう。
ここでは、 Web アプリケーションビルドと Windows アプリケーションビルドの2通りの方法を紹介していく。
Web アプリケーションビルド
Web アプリケーションビルドは、どの OS からでも可能である。
- 以下のコマンドを実行する。ただし、このコマンドでビルを行う際は、プロジェクトディレクトリの構成が先述の通りになっているかを確認すること。
> flet build web
実行後は、何も問題がなければ以下のような表示が出るはず(各 OS での初回のビルド時は、 Flutter SDK のインストールが行われる)。
[15:33:46] Flutter 3.27.3 installed ✅
[15:33:49] Created Flutter bootstrap project from gh:flet-dev/flet-build-template with ref "0.26.0" ✅
[15:33:57] Packaged Python app ✅
[15:33:59] Customized app icons and splash images ✅
[15:34:02] Generated app icons ✅
[15:34:05] Generated splash screens ✅
[15:34:10] Built web app ✅
Copied build to build\web directory ✅
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Successfully built your web app! 🥳 Find it in build\web directory. 📁 │
│ Run python -m http.server --directory build\web command to start dev web server with your app. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
- 続いて、以下のコマンドを実行して Web アプリケーションを起動する。
> python -m http.server --directory build\web
その後、http://localhost:8000にアクセスしてみよう。
先ほどflet runを実行したときに現れたウィンドウと同じ内容が表示された。これでビルド作業は終了である。
ビルドした Web アプリケーションのホスティングについては、公式ドキュメント等を参照のこと。
Windows アプリケーションビルド
Windows のアプリケーションビルドを行う際は、開発者モードを有効にした上で、前提要件を満たしている必要がある。

- 以下のコマンドで、その前提要件を満たしているか確認する。
> flutter doctor
例えば、こんな表示になるはず。
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.24.3, on Microsoft Windows [Version 10.0.22631.4890], locale ja-JP)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[X] Android toolchain - develop for Android devices
X Unable to locate Android SDK.
Install Android Studio from: https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK components.
(or visit https://flutter.dev/to/windows-android-setup for detailed instructions).
If the Android SDK has been installed to a custom location, please use
`flutter config --android-sdk` to update to that location.
[√] Chrome - develop for the web
[!] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.12.2)
X Visual Studio is missing necessary components. Please re-run the Visual Studio installer for the "Desktop development with C++" workload, and include these components:
MSVC v142 - VS 2019 C++ x64/x86 build tools
- If there are multiple build tool versions available, install the latest
C++ CMake tools for Windows
Windows 10 SDK
[!] Android Studio (not installed)
[√] VS Code (version 1.97.1)
[√] Connected device (3 available)
[√] Network resources
! Doctor found issues in 3 categories.
色々出てきたが、指示は丁寧なので書いてあることに従えばよい。
今回の例でいうと
- Android Studio が未インストール
- Visual Studio に「C++ によるデスクトップ開発」ワークロードが存在しない
ということなので、順に入れていく。
Android Studio のインストール
- Android Studio の公式ページから、 Android Studio のインストーラーをダウンロード。
色々チェック項目が出てくるが、とりあえずデフォルトのままで進めて OK 。
- インストールが終わったら、続けて Android Studio のセットアップを行う。
こちらも色々項目が存在するが、デフォルトのまま進めてしまおう。ここは結構時間がかかった。
さらに、 Android Studio のコマンドラインツールをインストールする。
- 上の画像の「More Actions」から「SDK Manager」を選択。
- 「Languages & Frameworks」から「Android SDK」を選択し、「SDK Tools」タブを開き、「Android SDK Command-line Tools (latest)」にチェックマークを入れて、「Apply」を押す。
- ここまで終わったら、一度
flutter doctorを実行してみる。
[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
Android toolchain の部分の表示が変わった。どうやらライセンスへの同意が必要らしい。
- 言われている通りコマンドを実行する。
> flutter doctor --android-licenses
全て終わるとこのような表示が出る。
All SDK package licenses accepted
Visual Studio に C++ デスクトップ開発関連のワークロードを追加する
続いて、 Visual Studio に C++ デスクトップ開発関連のワークロードを追加しよう。なお、まだ Visual Studio がインストールされていない場合は、以下のリンクからインストールしよう。
- Visual Studio Installer を起動し、インストール済みのバージョンの「変更」を押す。

- 終わったら、再度
flutter doctorを実行。
Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.24.3, on Microsoft Windows [Version 10.0.22631.4890], locale ja-JP) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 35.0.1) [√] Chrome - develop for the web [√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.12.2) [√] Android Studio (version 2024.2) [√] VS Code (version 1.97.1) [√] Connected device (3 available) [√] Network resources • No issues found!
このような表示が出れば OK 。
ビルド
ようやく Windows ビルドの準備が整った。
- 次のコマンドで Windows アプリケーションビルドを行う。
> flet build windows
[15:17:02] Flutter 3.27.3 installed ✅ [15:17:09] Created Flutter bootstrap project from gh:flet-dev/flet-build-template with ref "0.26.0" ✅ [15:17:26] Packaged Python app ✅ [15:17:29] Customized app icons and splash images ✅ [15:17:32] Generated app icons ✅ [15:19:05] Built Windows app ✅ [15:19:07] Copied build to build\windows directory ✅ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Successfully built your Windows app! 🥳 Find it in build\windows directory. 📁 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
実行ファイルはbuild\windows内にflet-test.exeとして格納されている。これを起動すると...
無事成功した。
おわりに
ということで今回は、 Python で GUI アプリケーションを手軽に作成できるフレームワーク「Flet」の紹介と、環境構築~ビルドまでの流れをまとめた。
このフレームワークはまだまだ開発段階にあり、コミュニティもホットである。今後の発展に期待したいところだ。
【AtCoder】ABC 385 D - Santa Claus 2 | 緑コーダーが解くAtCoder
実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: 1171
問題概要
2次元平面上の 点
に家が建っている。最初、点
にサンタクロースがいて、列
に従って以下の行動を行う。
の順に以下のように移動する。なお、現在サンタクロースがいる点を
とする。
Uなら、から
に直線で移動する。
Dなら、から
に直線で移動する。
Lなら、から
に直線で移動する。
Rなら、[tex (x,y)] からに直線で移動する。
行動を終えたあとにサンタクロースがいる点と、行動により通過または到達した家の数を求めよ。ただし、同じ家を複数回通過または到達してもそれらは重複して数えない。
制約
- 与えられる数値は全て整数。
は相異なる。
に家は建っていない。
考察
サンタクロースは各行動で軸平行な向きにしか移動しないので、家の位置もそれぞれの軸の位置でまとめて管理しておくことにする。具体的には、
座標が
である家の
座標の集合
座標が
である家の
座標の集合
というような、map<long long, set<long long>>型のデータ構造を用意する。
こうすることで、例えば とサンタクロースが移動するとき、通過・到達した家の数は
の中の
以上
以下の要素数 (
とする) に対応することになる。
ここで、mapのvalueをsetにしていることがポイントで、 このsetに対して をキーとして二分探索 (
lower_bound) し、そこからイテレータを1個ずつずらしていくことで、 時間で
個の要素を列挙できる。
列挙した各要素はsetからeraseすることで、家の二重カウントを防ぐ。
以上の操作を各方向に対して行っていくことで、全体としては でこの問題を解くことができる。
以下、実装時の注意点を少しだけ書いておく。
まず、setに対して二分探索を行うときにstd::set::lower_boundを使うようにするということだ。
普段使っているstd::lower_boundをsetに対して用いると、実行速度が極端に遅くなってしまう (1敗) 。詳しくは以下の記事を参照。
また、std::set::eraseが削除された次の要素を指すイテレータを返すという仕様があるため、通過した家をsetから除くときは
itr = houses_on_x[pos_x].erase(itr);
のように書いている。
コード
#include <bits/stdc++.h> using namespace std; using ll = long long; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) // ======================================== // int main() { int N, M; ll Sx, Sy; cin >> N >> M >> Sx >> Sy; map<ll, set<ll>> houses_on_x, houses_on_y; rep(i, 0, N) { ll X, Y; cin >> X >> Y; houses_on_x[X].insert(Y); houses_on_y[Y].insert(X); } ll pos_x = Sx, pos_y = Sy, ans = 0; rep(i, 0, M) { char D; ll C; cin >> D >> C; ll dx = 0, dy = 0; if (D == 'U') dy = C; else if (D == 'D') dy = -C; else if (D == 'L') dx = -C; else if (D == 'R') dx = C; if (dx == 0) { ll min_y = min(pos_y, pos_y + dy); ll max_y = max(pos_y, pos_y + dy); auto itr = houses_on_x[pos_x].lower_bound(min_y); while (itr != houses_on_x[pos_x].end() && *itr <= max_y) { ans++; houses_on_y[*itr].erase(pos_x); itr = houses_on_x[pos_x].erase(itr); } } else if (dy == 0) { ll min_x = min(pos_x, pos_x + dx); ll max_x = max(pos_x, pos_x + dx); auto itr = houses_on_y[pos_y].lower_bound(min_x); while (itr != houses_on_y[pos_y].end() && *itr <= max_x) { ans++; houses_on_x[*itr].erase(pos_y); itr = houses_on_y[pos_y].erase(itr); } } pos_x += dx; pos_y += dy; } cout << pos_x << " " << pos_y << " " << ans << endl; }
実装時間: 30分
類題としては ABC370-D が挙げられるか?
【AtCoder】ABC 385 C - Illuminate Buildings | 緑コーダーが解くAtCoder
実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: 446
問題概要
棟のビルが等間隔に一列に並んでおり、手前から
番目のビルの高さは
である。次の条件をともに満たすようにいくつかのビルを選んで電飾で飾るとき、最大でいくつのビルを選ぶことができるか求めよ。なお、ちょうど1つのビルを選んだときは条件を満たすとみなす。
- 選んだビルたちは高さが等しい。
- 選んだビルたちは等間隔に並んでいる。
制約
- 入力は全て整数。
考察
以下のような dp テーブル (dp<map<int, int>>) を用意して、動的計画法により解く。
手前から
番目のビルを含み、間隔が
で高さが
のビルを選んでいくときの最大選択数
遷移は以下のようになる。
- 各
に対して、ビル
が含まれるグループを見ていく。
の場合はビル
は選べないのでスキップするとして、そうでないとき、
に
が含まれるとき、
のグループにビル
を追加することができるので、
と遷移する。
- そうでないとき、ビル
の2つで新たにグループを作ることになるので、
と遷移する。
- 上の2つの判定後、
ans = max(ans, dp[j][d])で更新する。
コード
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) template <typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false)); } // ======================================== // int main() { int N; cin >> N; vector<int> H(N); rep(i, 0, N) cin >> H[i]; vector<map<int, int>> dp(N); int ans = 1; rep(i, 0, N) { rep(j, 0, i) { if (H[j] == H[i]) { int d = i - j; if (dp[j].contains(d)) { dp[i][d] = dp[j][d] + 1; } else { dp[i][d] = 2; } chmax(ans, dp[i][d]); } } } cout << ans << endl; }
実装時間: 15分
【AtCoder】ABC 385 B - Santa Claus 1 | 緑コーダーが解くAtCoder
実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: 77
問題概要
のマス目があり、上から
行目、左から
列目のマスをマス
と表す。マス
は
#のとき通行不可能、. のとき通行可能であり家が建っていない、@のとき通行可能であり家が建っていることを表す。
最初、マス にサンタクロースがおり、文字列
に従って以下の行動を行う。
- 現在サンタクロースがいるマスを
とする。
について、
Uかつマスが通行可能ならマス
に移動する。
Dかつマスが通行可能ならマス
に移動する。
Lかつマスが通行可能ならマス
に移動する。
Rかつマスが通行可能ならマス
に移動する。
- それ以外の場合、マス
に留まる。
行動を終えたあとにサンタクロースがいるマスと、行動により通過または到達した家の数を求めよ。ただし、同じ家を複数回通過または到達してもそれらは重複して数えない。
制約
- 与えられる数値は全て整数。
- 全ての
について
# - 全ての
について
# .は
U,D,L,Rのいずれかからなる長さ以上
以下の文字列。
考察
基本的には、サンタクロースの動きをシミュレーションしていき、各行動毎に到着したマスが家かどうかを判定してカウントしていけばよい。
ただし、「同じ家を複数回通過または到達してもそれらは重複して数えない」とあるので、一度訪れた家のマスは@から.に置き換えるなどの処理が必要。家を破壊する物騒なサンタ...
コード
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) // ======================================== // int main() { int H, W, X, Y; cin >> H >> W >> X >> Y; X--, Y--; vector<string> S(H); rep(i, 0, H) cin >> S[i]; string T; cin >> T; int cnt = 0; rep(i, 0, T.size()) { if (T[i] == 'U') { if (X == 0 || S[X - 1][Y] == '#') continue; X--; } else if (T[i] == 'D') { if (X == H - 1 || S[X + 1][Y] == '#') continue; X++; } else if (T[i] == 'L') { if (Y == 0 || S[X][Y - 1] == '#') continue; Y--; } else if (T[i] == 'R') { if (Y == W - 1 || S[X][Y + 1] == '#') continue; Y++; } if (S[X][Y] == '@') { cnt++; S[X][Y] = '.'; } } cout << X + 1 << " " << Y + 1 << " " << cnt << endl; }
実装時間: 5分