- 投稿日:2020-03-24T22:00:15+09:00
JavaアプリをDockerイメージにするならjibを使うと便利だよ
メリットの多いDocker
コンテナ化技術の一つであるDockerですが、使ってみると数多くのメリットがあります。
実際のメリットについてはネットで検索すると色々出てくるので、この記事ではDockerそのものに関する解説はいたしません。
ただ個人的に強調しておきたいのは、本番環境などへ直接Javaのモジュールやアーカイブなどを配置するようなデプロイしている場合、Dockerイメージをリポジトリにpushするという作業に変えることでたくさんの恩恵を得ることができると思います。Dockerの面倒さ
メリットの多いDockerですが面倒な面もあります。
Dockerfileを書かないといけない。書くためのコマンドやベストプラクティスも学習するとなるとそれなりに面倒です。
ローカル開発環境がWindowsの場合にDocker環境を構築するのも面倒です。jibが解決
もしDockerコンテナ上で動くアプリケーションがJavaで開発されている場合、Googleが提供しているjibを利用することでDockerにまつわるいくつかの面倒さから解放されます。
jibを利用するにあたって必要なのは(gradleを使っている場合)
build.gradle
にplugins { id 'com.google.cloud.tools.jib' version '2.1.0' }という設定だけです。
そして仮に
docker-image-to-push/1.0.0
というイメージ名でpushしたい場合はjib.to.image = 'docker-image-to-push/1.0.0'と
build.gradle
に記述するだけです。
これでgradle jib
と実行すればDockerイメージが作成されpushされます。
驚くべきはたったこれだけの設定でいいことと、ローカルにDocker環境が必要でないことです。
もしDocker Hubにアカウントを持っているならjib.to { auth { username 'account' password 'pass' } image 'account/repository:1.0.0' }のように
build.gradle
に設定することですぐにjibを試すことができます。
(account, pass, repositoryの部分は環境に合わせて変更してください)上記の例でわかるようにほとんど設定のいらないjibですが、色々設定することも可能です。
まず設定しておいてほしいのは(JavaのソースファイルのエンコーディングがUTF-8の場合)jib.container.environment = [JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8']という設定です。
これがないとソースファイル中に日本語がある場合にエラーになってしまいます。
設定できる内容については configurationから確認できます。以上、jibの紹介でした。
- 投稿日:2020-03-24T21:34:53+09:00
【Java】日付から差分を算出したい
開始日付、終了日付をもとに処理時間を算出したかった。
date.javaimport java.text.SimpleDateFormat; import java.util.Date; public class date { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date start = null; Date end = null; // Date型に変換 try { start = sdf.parse("2020/03/23 12:30:12"); end = sdf.parse("2020/03/23 12:31:12"); } catch (java.text.ParseException e) { e.printStackTrace(); } long timeStart = start.getTime(); long timeEnd = end.getTime(); long processTime = timeEnd - timeStart; // ミリ秒 System.out.println(processTime); } }実行結果:
60000
- 投稿日:2020-03-24T17:19:18+09:00
spring-batchでメタデータとstepで異なるTransactionManagerを使う
spring-batchで2つ以上のデータソースを使う場合、step定義作成時に
StepBuilderHelper#transactionManager
でトランザクションマネージャを指定する。以下は設定例。
import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.sql.DataSource; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.support.ListItemReader; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.batch.BatchDataSource; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @SpringBootApplication @EnableBatchProcessing public class Application { @Bean @BatchDataSource public DataSource springBatchDs() { // 1 return DataSourceBuilder .create() .url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE") .username("sa") .password("") .build(); } @Bean @Primary public DataSource primaryDs() { // 2 return DataSourceBuilder .create() .url("jdbc:oracle:thin:system/oracle@localhost:11521/XEPDB1") .username("system") .password("oracle") .build(); } @Bean public Job job(JobBuilderFactory jobs, Step s1) { return jobs.get("myJob") .incrementer(new RunIdIncrementer()) .start(s1) .build(); } @Bean("stepTransactionManager") public PlatformTransactionManager stepTransactionManager(DataSource primaryDs) { // 3 return new DataSourceTransactionManager(primaryDs); } @Bean public Step step1(StepBuilderFactory steps ,DataSource dataSource // 4 ,@Qualifier("stepTransactionManager") PlatformTransactionManager transactionManager) { ItemReader<Integer> reader = new ListItemReader<Integer>( IntStream.range(1, 1001).boxed().collect(Collectors.toList())); JdbcTemplate jdbc = new JdbcTemplate(dataSource); TaskletStep build = steps.get("step1") // 5 .transactionManager(transactionManager) .<Integer, Integer>chunk(10) .reader(reader) .writer(list -> { list.forEach(s -> { jdbc.update("update aaa set user_id = user_id + 1"); }); }) .build(); return build; } public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args); } }
- サンプルとして、メタデータの保存先はH2を使用する。終了時に消えるので、事実上メタデータは永続化しない。
- メインのデータソース。
- step処理用のトランザクションマネージャ。メインのデータソースを使用する。メインのデータソースに
@Primary
がついてるので、このメソッドの引数にはそちらのデータソースが来る。@Qualifier
で上で作成したトランザクションマネージャを得る。@Qualifier
が無いと、spring-batchのSimpleBatchConfiguration
が内部的に作成するtransactionManager
が入ってくる。- step定義作成時に上で作成したトランザクションマネージャを指定する。これでメタデータ処理とstep処理とで異なるトランザクションマネージャが使用される。
- 投稿日:2020-03-24T13:51:12+09:00
【Android】 Broadcast Recieverでintentを受け取る
Broadcast Recieverとは?
- BroadcastReceiver(ブロードキャストレシーバー)とは、ブロードキャストしたインテントを受け取る仕組みのこと
- OSのAndroidシステム側でブロードキャストされるイベント(スクリーンのON/OFFなど)以外でも、自分で作成したアプリでも独自のインテントを生成してブロードキャストできる
intentとは?
- intentとは、アプリケーションの中の1つ1つの機能のこと。たとえばアプリケーション同士や、アプリケーションとウィジェット、アプリケーションとシステムを橋渡しする仕組みのこと
- BroadcastRecieverを使わずとも、intentだけでも利用が可能で、例えば電話をかけたりカメラを起動することもできる
- BroadcastRecieverでは、例えばアプリをインストールした際などに発生するintentを検知することができます。例えば...
- スクリーンのON/OFFを検知する
- 電池の状態を検知する
- アプリのインストールを検知する
- 振動を検知する 等
使ってみよう
アクティビティからサービスを起動 → サービスでレシーバーを登録 という流れでやってみました
MyReceiver.javapublic class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction() != null) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { Log.d("registerReceiver: ", "ON"); } if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { Log.d("registerReceiver: ", "OFF"); } } } }MyActivity.javapublic class MyActivity extends Activity { Button startButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startButton = (Button)findViewById(R.id.start_button); startButton.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { getActivity().startForegroundService(new Intent(getActivity(), MentalHealthService.class)); } else { getActivity().startService(new Intent(getActivity(), MentalHealthService.class)); } } } }MyService.javapublic class MyService extends Service { private MyReceiver mReceiver; private IntentFilter mIntentFilter; @Override public void onCreate() { super.onCreate(); // Receiver登録を実行 registerScreenReceiver(); } @Override public void onDestroy() { super.onDestroy(); // Receiverを解除 unregisterReceiver(mReceiver); } @Override public int onStartCommand(Intent intent, int flags, int startId) { startInForeground(); return START_STICKY; } // receiverを登録 private void registerScreenReceiver() { mReceiver = new MyReceiver(); mIntentFilter = new IntentFilter(); mIntentFilter.addAction(Intent.ACTION_SCREEN_ON); mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(mReceiver, mIntentFilter); } // サービスをアライブさせるために通知を生成 private void startInForeground() { Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(getResources().getString(R.string.app_name)) .setContentText("test notification") .setTicker(getResources().getString(R.string.app_name)) .setContentIntent(pendingIntent); Notification notification = builder.build(); if (Build.VERSION.SDK_INT >= 26) { NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID , NOTIFICATION_CHANNEL_NAME , NotificationManager.IMPORTANCE_DEFAULT); channel.setDescription(NOTIFICATION_CHANNEL_DESC); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) { notificationManager.createNotificationChannel(channel); } } startForeground(NOTIFICATION_ID, notification); } }解説
- MyReceiverのonReceiveにて、intent.getAction()が登録したintentFilterのアクションと合致したらログを出す様に処理
- MyActivityにボタンを設置。ボタンを押すとサービスを起動するという単純な構造
- MyService内でReceiverの登録を行なっている。startInForegroundはForegroundService用でとりあえず実装(あまり気にしなくていいです)
- サービスが起動している間に、スクリーンをOn/Offにしたらその度にログが出る
備考
- 最ライフログやヘルスケアのアプリをよく見るので、どんな風にデータを集計しているのか気になったところ、intentを使っているパターンが多い様に感じたので調査してみた
- 今回紹介した以外にも、intentはものすごく沢山あるので、アプリによっては有用なものもある...かもしれない
- 投稿日:2020-03-24T12:48:45+09:00
【AtCoder Beginner Contest 152 C問題】TLEする人の書き方と、printデバッグを入れる位置のコツ
printデバッグを入れる位置で混乱していませんか?
お久しぶりです。宇宙意志です。
今回はいつものように書き捨てのSeleniumではなく、ABC152-C問題を見ていきたいと思います。別にリングフィット購入の自動転売で大儲けしようとしたのにLambdaが動かなくてキレていた訳ではないです。
今回の趣旨ですが考え方とデバッグの位置を說明です、まだTLE(時間切れ)で解けていません。
問題の概要
1.N個の順列がバラバラに並んでいます
2.前からiを走査させます
3.走査中のiよりも後ろにある順列全て(ここではjと定義されてます)と比較して、j!>=i! が常に成立するならi1つに付き1カウント
4.条件が成立するiの合計カウントを求めるという問題です。
解法の考え方
1.まずこの問題を聞いた時に、2重for文を使って配列のiの位置を記憶させて、jを配列として左端からiの位置まで走査すれば良いな、
というのはすぐに思いつくと思います。2.順列の並べ替え
ここもすぐに気付かなければなりません。この問題はタイムアウトを狙っています。ですから、
順列は常に n >= m の時常に n! >= m! が成立するので、n>mの確認をすれば順列を計算する必要はないと分かります。
これで問題は単なる大小比較になりました。ここで順列の計算を始めると大量にTLE(時間切れ)が出てしまいます。また、j用の配列を左端からiまでを走査する時、途中で条件不成立ならbreakさせよう。(TLEを避けるため)というのも思いつきます。
3.いつカウントを加えればいいの?
これについて考えなければなりません。
jの配列を全て比較し終わった時に条件不成立ならcount++したい。という事は、j用のfor分をぐるぐる回してバターになっている
時はcountはいじれない。最後に加えたい。という事は、フラグ(良い名前をつけましょう)を使って保持して、for文が終わった時点で
count++するかしないか判定すれば良い。
ここまで分かれば解けたも同然です。
解けたも同然というのは内心的効果意思であって、解けたという事実を示したものでありません。以下、コメントを入れたソースを見ていきましょう。
import java.io.*; import java.util.*; public class Main { void submit() { int N = nextInt(); int[] P = new int[N]; for(int i=0; i<N; i++){ P[i] = nextInt(); }上は入力値を入れてるだけですね。順列にする必要がないのは上記で説明した通りです。
int total = 1; if(N==1){ out.println(total); return; }最初に与えられた総数が1の時は特別で、i,jと2重配列を作りようがないのです。
こういう所で無理してネストを増やしてがんばってはいけません。
ソースを見やすくします。最初に貰った数値が1つだけの時はreturnして終了させて、ネストを減らします。for(int i=1; i<N; i++){ /*** big_flgとかいう分かりにくい名前はやめて、リーダブルコードを読みましょう!!! ***/ boolean big_flg= true; /*** jを走査しています。i以下を比較する配列なので、初期位置は int j=0になります ***/ for(int j=0; j<=i; j++){ /*** 今回見て欲しいデバッグ部分1です。比較対象になる整数を全て出力しています。 ***/ out.println("P[i]: "+ P[i]); out.println("P[j]: "+ P[j]); if(P[i]<=P[j]){ big_flg = true; }else{ big_flg = false; /*** 計算量を減らすために、駄目だと分かり次第breakして2重ループを抜けて次のiに進みます。 ちなみにcontinueも処理とネスト減らしの為によく使います ***/ break; } }大体コメントに書いたのですが、for文の2重配列で比較しているだけです。で、問題は、「エラーが出た時どうすれば良いのかわからないよドラえもん!」という方です。
今回記載しているように、常にデバッグ値は急いでいても、「"名前:数値 」という形で出してあげましょう。
out.println("P[i]: "+ P[i]);
out.println("P[j]: "+ P[j]);このように書く事で、いわゆるprintデバッグの精度を上げる事が出来ます。
/*** 今回見て欲しいデバッグ部分2です。結局出てきた時のフラグはどっちなの? 成立? 不成立?を見ています ***/ out.println("big_flg: "+ big_flg); if(big_flg){ total++; } } out.println(total); }で、のび太さんはまたデバッグする時に__どこでデバッグするの? どの位置でフラグの値を知りたいの?という事が重要です。
これに関しては一言でいうと、if文の前に入れましょう。大体if文に入る前に間違ってます。
printデバッグはどこに入れれば良いのか
二重for文で実装時に間違えるケースはそんなに多くなくて、
1.forの境界値で間違えてる
2.if文の条件が間違えてる
3.if文に入るフラグが間違えてるの3つです。1.2は余程複雑な条件でなければすぐに気付きます。そもそも複雑な条件は作ってはいけません。
だから3です。C問題が解けない方は、3のデバッグの仕方を鍛えましょう、と言っています。以下はトップコーダーのパクリなのでおまじない。気にしません。わたしのソースにSystem.out.println()ではなく、
out.println()で出力しているのはこのおまじないによります。void preCalc() { } void stress() { } void test() { } Main() throws IOException { br = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(System.out); preCalc(); submit(); //stress(); //test(); out.close(); } static final Random rng = new Random(); static int rand(int l, int r) { return l + rng.nextInt(r - l + 1); } public static void main(String[] args) throws IOException { new Main(); } BufferedReader br; PrintWriter out; StringTokenizer st; String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { throw new RuntimeException(e); } } return st.nextToken(); } String nextString() { try { return br.readLine(); } catch (IOException e) { throw new RuntimeException(e); } } int nextInt() { return Integer.parseInt(nextToken()); } long nextLong() { return Long.parseLong(nextToken()); } double nextDouble() { return Double.parseDouble(nextToken()); }}
```まとめたソースは以下。
ちなみにこれでもTLEになりますた? ボスケテ
import java.io.*; import java.util.*; public class Main { void submit() { int N = nextInt(); int[] P = new int[N]; for(int i=0; i<N; i++){ P[i] = nextInt(); } int total = 1; if(N==1){ out.println(total); return; } for(int i=1; i<N; i++){ boolean big_flg= true; for(int j=0; j<=i; j++){ if(P[i]<=P[j]){ //out.println("P[i]: "+ P[i]); //out.println("P[j]: "+ P[j]); big_flg = true; }else{ big_flg = false; break; } } //out.println("big_flg: "+ big_flg); if(big_flg){ total++; } } out.println(total); } void preCalc() { } void stress() { } void test() { } Main() throws IOException { br = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(System.out); preCalc(); submit(); //stress(); //test(); out.close(); } static final Random rng = new Random(); static int rand(int l, int r) { return l + rng.nextInt(r - l + 1); } public static void main(String[] args) throws IOException { new Main(); } BufferedReader br; PrintWriter out; StringTokenizer st; String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { throw new RuntimeException(e); } } return st.nextToken(); } String nextString() { try { return br.readLine(); } catch (IOException e) { throw new RuntimeException(e); } } int nextInt() { return Integer.parseInt(nextToken()); } long nextLong() { return Long.parseLong(nextToken()); } double nextDouble() { return Double.parseDouble(nextToken()); } }という訳で、今回は以上です。
最期に
java8で回答された優秀な方の解法がたんまりありますので、table右側の詳細で見てみて下さい。
https://atcoder.jp/contests/abc152/submissions?f.Task=abc152_c&f.Language=3016&f.Status=AC&f.User=今回は以上になります。
・デバッグの位置に気を遣おうね
・名前は表示しようね、
という2点でした。それでは、また会いましょう。
- 投稿日:2020-03-24T12:48:45+09:00
【AtCoder Beginner Contest 152 C問題】TLEについてと、printデバッグ位置のコツ
printデバッグを入れる位置で混乱していませんか?
お久しぶりです。宇宙意志です。
今回はいつものように書き捨てのSeleniumではなく、ABC152-C問題を見ていきたいと思います。別にリングフィット購入の自動転売で大儲けしようとしたのにLambdaが動かなくてキレていた訳ではないです。
今回の趣旨ですが考え方とデバッグの位置を說明です、まだTLE(時間切れ)で解けていません。
問題の概要
1.N個の順列がバラバラに並んでいます
2.前からiを走査させます
3.走査中のiよりも手前にある順列全て(ここではjと定義されてます)と比較して、j!>=i!j>=iが常に成立するならi 1つに付き1カウント
4.条件が成立するiの合計カウントを求めるという問題です。
解法の考え方
1.まずこの問題を聞いた時に、2重for文を使って配列のiの位置を記憶させて、
jを配列として左端からiの位置まで走査すれば良いな、というのはすぐに思いつくと思います。
2.順列の並べ替え
ここもすぐに気付かなければなりません。この問題はタイムアウトを狙っています。ですから、
順列は常に n >= m の時常に n! >= m! が成立するので、n>mの確認をすれば順列を計算する必要はないと分かります。
これで問題は単なる大小比較になりました。ここで順列の計算を始めると大量にTLE(時間切れ)が出てしまいます。@mui_nyan さまからコメントでご指摘が合ったためyoutubeの解説動画で確認しました。順列という言葉に
特に意味はなく、普通の順番のランダムの数値という事のようです。申し訳ございませんでした。
(2020/03/24 15:56 修正)また、j用の配列を左端からiまでを走査する時、途中で条件不成立ならbreakさせよう。(TLEを避けるため)
というのも思いつきます。3.いつカウントを加えればいいの?
これについて考えなければなりません。
jの配列を全て比較し終わった時に条件不成立ならcount++したい。という事は、
j用のfor分をぐるぐる回してバターになっている時はcountはいじれない。
最後に加えたい。という事は、フラグ(良い名前をつけましょう)を使って保持して、
for文が終わった時点でcount++するかしないか判定すれば良い。
ここまで分かれば解けたも同然です。
解けたも同然というのは内心的効果意思であって、解けたという事実を示したものでありません。(民法)以下、コメントを入れたソースを見ていきましょう。
import java.io.*; import java.util.*; public class Main { void submit() { int N = nextInt(); int[] P = new int[N]; for(int i=0; i<N; i++){ P[i] = nextInt(); }上は入力値を入れてるだけですね。
順列にする必要がないのは上記で説明した通りです。int total = 1; if(N==1){ out.println(total); return; }最初に与えられた総数が1の時は特別で、i,jと2重配列を作りようがないのです。
こういう所で無理してネストを増やしてがんばってはいけません。
ソースを見やすくします。最初に貰った数値が1つだけの時はreturnして終了させて、ネストを減らします。※以下のソースコードは解法の追記で書き直しています
for(int i=1; i<N; i++){ /*** big_flgとかいう分かりにくい名前はやめて、リーダブルコードを読みましょう!!! ***/ boolean big_flg= true; /*** jを走査しています。i以下を比較する配列なので、初期位置は int j=0になります ***/ for(int j=0; j<=i; j++){ /*** 今回見て欲しいデバッグ部分1です。比較対象になる整数を全て出力しています。 ***/ out.println("P[i]: "+ P[i]); out.println("P[j]: "+ P[j]); if(P[i]<=P[j]){ big_flg = true; }else{ big_flg = false; /*** 計算量を減らすために、駄目だと分かり次第breakして2重ループを抜けて次のiに進みます。 ちなみにcontinueも処理とネスト減らしの為によく使います ***/ break; } }大体コメントに書いたのですが、for文の2重配列で比較しているだけです。で、問題は、「エラーが出た時どうすれば良いのかわからないよドラえもん!」という方です。
今回記載しているように、常にデバッグ値は急いでいても、「"名前:数値 」という形で出してあげましょう。
out.println("P[i]: "+ P[i]);
out.println("P[j]: "+ P[j]);このように書く事で、いわゆるprintデバッグの精度を上げる事が出来ます。
/*** 今回見て欲しいデバッグ部分2です。結局出てきた時のフラグはどっちなの? 成立? 不成立?を見ています ***/ out.println("big_flg: "+ big_flg); if(big_flg){ total++; } } out.println(total); }で、のび太さんはまたデバッグする時にどこでデバッグするの? どの位置でフラグの値を知りたいの?という事が重要です。
これに関しては一言でいうと、if文の前に入れましょう。大体if文に入る前に間違ってます。printデバッグはどこに入れれば良いのか
二重for文で実装時に間違えるケースはそんなに多くなくて、
1.forの境界値で間違えてる
2.if文の条件が間違えてる
3.if文に入るフラグが間違えてるの3つです。1.2は余程複雑な条件でなければすぐに気付きます。
そもそも複雑な条件は作ってはいけません。
だから3です。C問題が解けない方は、3のデバッグの仕方を鍛えましょう、と言っています。以下はトップコーダーのパクリなのでおまじない。気にしません。わたしのソースにSystem.out.println()ではなく、
out.println()で出力しているのはこのおまじないによります。void preCalc() { } void stress() { } void test() { } Main() throws IOException { br = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(System.out); preCalc(); submit(); //stress(); //test(); out.close(); } static final Random rng = new Random(); static int rand(int l, int r) { return l + rng.nextInt(r - l + 1); } public static void main(String[] args) throws IOException { new Main(); } BufferedReader br; PrintWriter out; StringTokenizer st; String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { throw new RuntimeException(e); } } return st.nextToken(); } String nextString() { try { return br.readLine(); } catch (IOException e) { throw new RuntimeException(e); } } int nextInt() { return Integer.parseInt(nextToken()); } long nextLong() { return Long.parseLong(nextToken()); } double nextDouble() { return Double.parseDouble(nextToken()); } }まとめたソースは以下。
ちなみにこれでもTLEになりますた? ボスケテ
abc152_c.javaimport java.io.*; import java.util.*; public class Main { void submit() { int N = nextInt(); int[] P = new int[N]; for(int i=0; i<N; i++){ P[i] = nextInt(); } int total = 1; if(N==1){ out.println(total); return; } for(int i=1; i<N; i++){ boolean big_flg= true; for(int j=0; j<=i; j++){ //out.println("P[i]: "+ P[i]); //out.println("P[j]: "+ P[j]); if(P[i]<=P[j]){ big_flg = true; }else{ big_flg = false; break; } } //out.println("big_flg: "+ big_flg); if(big_flg){ total++; } } out.println(total); } void preCalc() { } void stress() { } void test() { } Main() throws IOException { br = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(System.out); preCalc(); submit(); //stress(); //test(); out.close(); } static final Random rng = new Random(); static int rand(int l, int r) { return l + rng.nextInt(r - l + 1); } public static void main(String[] args) throws IOException { new Main(); } BufferedReader br; PrintWriter out; StringTokenizer st; String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { throw new RuntimeException(e); } } return st.nextToken(); } String nextString() { try { return br.readLine(); } catch (IOException e) { throw new RuntimeException(e); } } int nextInt() { return Integer.parseInt(nextToken()); } long nextLong() { return Long.parseLong(nextToken()); } double nextDouble() { return Double.parseDouble(nextToken()); } }という訳で、今回は以上です。
最期に
java8で回答された優秀な方の解法がたんまりありますので、table右側の詳細で見てみて下さい。
https://atcoder.jp/contests/abc152/submissions?f.Task=abc152_c&f.Language=3016&f.Status=AC&f.User=今回は以上になります。
・デバッグの位置に気を遣おうね
・名前は表示しようね
という2点でした。それでは、また会いましょう。
解法の追記(2020/03/24 15:57)
数時間ぶりにお久しぶりです。宇宙意志です。
@swordone さまより (i番目までの最小値)>(i+1番目の値)の個数を数えれば済む というアドバイスを頂きましたので、
解説動画も見て確認しました。上記の手法だとTLEしないという事です。という事で実装しました。min を保持して、そこから走査すれば計算量が減らせるという事です。以下のようになります。
int min=P[0]; for(int i=1; i<N; i++){ if(min >= P[i]){ min = P[i]; count++; } } out.println(count); }大分スッキリしましたね! これがパッと思いつけば良いんですが、本番では工夫せずに素直に実装してTLEを出してしまうのが筆者の実力という事でしょうか……ぴえん?
という事で、コメント頂いたお二人の方、ありがとうございました!
追々記(2020/03/24 20:30)
java用にソースのシンタックスハイライト(今知った)を修正して頂きました! @altさま、ありがとうございます!?
- 投稿日:2020-03-24T08:19:16+09:00
java bronze silver合格
- 投稿日:2020-03-24T03:36:29+09:00
GCPでJava版Minecraftサーバを建てた話(ついでにホワイトリストも設定)
はじめに
前回建てた ubuntu のサーバにそのまま Java 版 Minecraft サーバを建てたときのメモです。
Java のチューニングについては書かないので別途調べてください┏(<:)AlwaysFree(一生無料)の「f1-micro」だとメモリサイズが小さすぎるので、事前に「n1-standard-1」に拡張しておきました。
1. Java 版 Minecraft サーバの構築
1.1. Java のインストール
サーバに SSH で接続して、root ユーザにスイッチしておきます。
suroot ユーザでログインしたら mkdir コマンドでサーバをインストールするディレクトリを作ります。
作ったら cd コマンドで移動します。mkdir /mcjava cd /mcjavaMinecraft サーバをインストールしようと思いましたが、Java が入ってなさそうなので先に Java を入れましょう。
apt install default-jre
ディスク容量がどうのこうのと聞かれたら y (yes)と打っといてください。
以上で Java のインストールは完了です。1.2. Minecraft サーバのインストール
続いて Minecraft サーバのインストールです。ダウンロード用 URL は事前に調べておきましょう。
[MINECRAFT]https://www.minecraft.net/ja-jp/download/server/リンクをコピーしたらダウンロードしましょう。
wget https://launcher.mojang.com/v1/objects/bb2b6b1aefcd70dfd1892149ac3a215f6c636b07/server.jarダウンロードしたらサーバを1回起動します。エラーで終了するはずです。
サーバをダウンロードしたディレクトリに eula.txt というテキストファイルができているはずなので、開いて中身を編集します。
EULA(エンドユーザーライセンス条項)に同意するか否かというファイルなので、同意する場合は eula=false の箇所を eula=true に書き換えましょう。
eula=trueこれでインストール完了です。
忘れてました。Java 版の Minecraft サーバはデフォルトで tcp/25565 ポートを使っているので GCP のファイアウォールルール設定でポートを開けておきましょう。
1.3. Minecraft サーバの起動
作ったディレクトリに移動してサーバ起動のコマンドを入力します。サーバを起動するための専用ユーザを作っておいても良いですが、今回はそのまま root ユーザで起動します。
ダウンロードしてきたファイルの名前が「server.jar」だった場合は以下のコマンドで起動します。cd /mcjava java -Xmx1024M -Xms1024M -jar server.jar nogui以上で起動します。
余談ですが、Java 版 Minecraft サーバは Java の仮想マシンJVM 上で、事前に設定したメモリを上限として起動します。そのため、ubuntu サーバの負荷だけを見ていても Minecraft が余裕で動いてるのかどうかは分かりません。
人が増えてきてメモリが足りてないなと思ったらサーバ起動コマンドの -Xmx や -Xms の数字を増やしてみましょう。チューニングについてはここでは説明しません。1.4. Minecraftサーバへの接続
統合版と一緒なので説明は省略。
1.5. Minecraftサーバの停止
統合版と一緒なので説明は省略。
1.6. ホワイトリストの設定
ホワイトリストも一緒に設定しておこうと思ってやってみたんだけども、統合版と少し仕様が違っていたのでメモっておきます。
統合版では存在しないプレイヤー名も自由にホワイトリストに登録できましたが、Java 版ではそれができないようです。以下の通り、JohnikiJoestar2 は登録できませんでした。
ということで、whitelist.json を直接編集することにしました。
ここでも更に罠が。統合版のホワイトリストはユーザの名前だけ分かれば whitelist.json の登録はできますが、Java 版では uuid が必須項目のようです。統合版のつもりでやってたので、ここで 10 分くらいハマりました。
uuid はサーバ側のログを見れば分かるのですが、これからログインしてくる新規ユーザのログは無いです。
ですが、Mojang が uuid を調べる Mojang API を提供しています。[Mojang API] https://api.mojang.com/users/profiles/minecraft/JohnikiJoestar
{"id":"1f843a4609ae4715a3b062a522193fa0","name":"JohnikiJoestar"}これでジョニキ・ジョースターの uuid も分かったのでホワイトリストに登録しましょう。
whitelist.json の形式は xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx なので、id を少し手直ししてファイルを編集します。[ { "uuid": "1f843a46-09ae-4715-a3b0-62a522193fa0", "name": "JohnikiJoestar" } ]ファイルを直接編集した場合は、whitelist をリロードしておきましょう。
whitelist reload