パナソニックプログラミングコンテスト(AtCoder Beginner Contest 195) B - Many Oranges
問題
考察
重要な点としては
- Aグラム以上Bグラム以下というみかんの重さに下限と上限があること。
- みかんの重さが整数とは限らない。
- 重さの合計がぴったりW(キログラム単位)であること。
みかんの個数Nが分かっている場合、
が成り立てば、N個のみかんで重さの合計をぴったりWにできる。
公式解説で
(この考察内だとを式変形したものと同じ)
を見たとき、なぜこれが成り立つと重さをぴったりWにできるんだ?と思ったが、
は1000Wをみかんの個数でN等分したときのみかん1個の重さ。
N個の中で1個の重さを増やすと他の1個以上のみかんの重さが減り、逆に1個の重さを減らすと他の1個以上のみかんの重さが増えます。
(なぜなら、重さの合計は変わらずWで、みかんの重さの配分を変えているだけだから。)
このN個のみかんの重さの配分をいくら変えても、を中心に重さの最小値Xと最大値Yの差が広がり
の範囲に当てはまらない場合が出てきてしまう。
なので、XとYの差が最も小さくなるがA以上、B以下であるかどうかを判定している。
制約の から
みかんの個数は1~1000000までの範囲だと分かる。
あとはこの範囲の中でみかんの個数で全探索をしながら、が成り立つかをチェックして
最大の個数、最小の個数を出力する。
AtCoder Beginner Contest 198 C - Compass Walking
問題
考察
- 原点(0, 0)からユークリッド距離でRずつしか移動できない。
- 移動先の座標は整数でなくてもよい。
- ぴったり(X, Y)にたどり着かなければならない。
これがこの問題を解くときに大事なポイントだと思われる。
原点(0, 0)と(X, Y)間のユークリッド距離がRで割り切れる場合は
単純にユークリッド距離をRで割れば何回で(X, Y)にたどり着くか分かる。
逆にユークリッド距離がRで割り切れない場合については
(X, Y)とのユークリッド距離がR未満になるまで、(X, Y)に向かって移動する。(移動後の座標を(x, y)とする)
その後、(x, y)からユークリッド距離でR移動した座標と(X, Y)からユークリッド距離でR移動した座標
この2つが一致する座標に移動する。
そうすると、現在の座標からユークリッド距離でR移動すれば(X, Y)にぴったりだどり着ける。
つまり余分に1回移動回数を増やすことで、対応できる。
以上から
sqrt(X^2 + Y^2) / R でちょうど割り切れる場合はsqrt(X^2 + Y^2) / R
割り切れない場合はceil(sqrt(X^2 + Y^2) / R)になる。
ただ、sqrt(X^2 + Y^2) < Rの場合については2になる。
Educational Codeforces Round 45
Editorial
A問題
問題
個の解説ボックスと個の団体チームが存在しており、各団体チームに均等に解説ボックスを割り当てたい
をで割り切れないときは、各団体チームに解説ボックスを割り当てることが出来ない
1つの解説ボックスを解体するのに burles支払う
1つの解説ボックスを建てるのに burles支払う
各団体チームに同じ数の解説ボックスを配布したい時、支払うburlesの最小値を求める
考察
解説ボックスの数を(団体チーム数)で割り切れるまで
解体する( burles支払う)か
建てる( burles支払う)かどちらか一方を行うことを考えると
が分かる
結果的に支払うburlesの最小値はになる
ソースコード
Submission #39089978 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.NoSuchElementException; public class A { long N,M; int A,B; private void solve() { N = nextLong(); M = nextLong(); A = nextInt(); B = nextInt(); long mod = N % M; long a = A * (M - mod); long b = B * mod; long ans = Math.min(a, b); out.println(ans); } }
B問題
問題
皿の上に体のバクテリアが存在する
番目のバクテリアの大きさはである
という定数が与えられる
番目のバクテリアは以下の条件を満たすとき、番目のバクテリアを食べることが出来る
食べられた番目のバクテリアは皿の上から消える(番目のバクテリアの大きさは変化しない)
最終的に皿の上に残存しているバクテリアの数の最小値を求める
考察
まず、大きさの昇順にソートする
は重複する場合があるので、同じはまとめる
大きさの昇順にを見ていき、を満たす場合
のバクテリアを除いていく
ソースコード
Submission #39101019 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class B { int N,K; ArrayList<Integer> a; private void solve() { N = nextInt(); K = nextInt(); a = new ArrayList<>(); for(int i = 0;i < N;i++) { a.add(nextInt()); } Collections.sort(a); TreeMap<Integer,Integer> map = new TreeMap<>(); for(int v : a) { if (map.containsKey(v)) { map.put(v, map.get(v) + 1); } else { map.put(v, 1); } } ArrayList<Integer> b = new ArrayList<>(); for(int v : map.keySet()) { b.add(v); } int ans = N; for(int i = 0;i < b.size() - 1;i++) { int j = i + 1; if (b.get(i) < b.get(j) && b.get(i) + K >= b.get(j)) { ans -= map.get(b.get(i)); } } out.println(ans); } }
C問題
問題
個の"(",")"のみが含まれる文字列が与えられる
2つの文字列を選び、文字列結合した結果が有効な括弧文字列であるペアの数を求める
でペアとが有効な括弧文字列である場合、どちらも別々でカウントする
でペアとが有効な括弧文字列である場合、1つとカウントする
考察
3つパターンで個の括弧文字列を分ける
- 既に有効な括弧文字列である場合
- 文字列の左から順に見ていく。")"を見ている時、その文字列内で"("と")"で対応できていない")"の数が1つ以上の場合
- 文字列の右から順に見ていく。"("を見ている時、その文字列内で"("と")"で対応できていない"("の数が1つ以上の場合
後は
上記のパターンをすべて足した数が解
ソースコード
Submission #39109916 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class C { int N; String[] brackets; private void solve() { N = nextInt(); brackets = new String[N]; for(int i = 0;i < N;i++) { brackets[i] = next(); } TreeMap<Integer, Integer> leftMinus = new TreeMap<>(); TreeMap<Integer, Integer> rightPlus = new TreeMap<>(); int zero = 0; for(int i = 0;i < N;i++) { char[] b = brackets[i].toCharArray(); int L = b.length; int depth = 0; int leftCnt = 0; for(int j = 0;j < L;j++) { if (b[j] == ')') { depth--; } else { depth++; } if (depth < 0) { leftCnt++; } } depth = 0; int rightCnt = 0; for(int j = L-1;j >=0;j--) { if (b[j] == '(') { depth--; } else { depth++; } if (depth < 0) { rightCnt++; } } if (leftCnt > 0 && rightCnt > 0) { continue; } if (leftCnt == 0 && rightCnt == 0) { zero++; continue; } if (leftCnt > 0) { if (leftMinus.containsKey(depth)) { leftMinus.put(depth, leftMinus.get(depth) + 1); } else { leftMinus.put(depth, 1); } } if (rightCnt > 0) { if (rightPlus.containsKey(depth)) { rightPlus.put(depth, rightPlus.get(depth) + 1); } else { rightPlus.put(depth, 1); } } } long ans = ((long)zero) * (zero); for(int m : leftMinus.keySet()) { if (rightPlus.containsKey(-m)) { ans += ((long) leftMinus.get(m)) * rightPlus.get(-m); } } out.println(ans); } }
D問題
問題
頂点数がの0と1を含む正方行列で表される無向グラフを考える
が1の場合は頂点と頂点は辺で繋がっていることを表す
無向グラフの連結成分の数が、無向グラフの補グラフ上の連結成分の数がである
無向グラフを表すことが出来るかを考える
※補グラフの説明:
補グラフ - Wikipedia
考察
とかのパターンで色々実験してみると
の場合はになることに気付く(の場合は)
これは補グラフの特徴として
無向グラフ上で辺が繋がっていない頂点同士はグラフの補グラフ上では辺で繋がることに基づく
例えば、の上で2つの連結成分が存在し
連結成分では頂点
連結成分では頂点を含むとする
の補グラフでは
頂点はと辺で繋がり、頂点もと繋がり、最終的にでは1つの連結成分となる
これででの場合は条件を満たす無向グラフを表すことが出来ないことが分かった
あとはの場合を考えると
連結成分の数がになるまで頂点同士を辺で繋げていけば良い
の場合もの時と同じ考えで解ける
特殊ケースがある
N = 2,A = 1,B = 1
N = 3,A = 1,B = 1
ソースコード
Submission #39414034 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.NoSuchElementException; public class D { int N,A,B; private void solve() { N = nextInt(); A = nextInt(); B = nextInt(); int min = Math.min(A ,B); if (min > 1) { out.println("NO"); return; } if ((N == 2 && A == 1 && B == 1) || (N == 3 && A == 1 && B == 1)) { out.println("NO"); return; } int[][] G = new int[N][N]; if (A == 1) { for(int i = 0;i < N;i++) { for(int j = 0;j < N;j++) { if (i == j) { G[i][j] = 0; } else { G[i][j] = 1; } } } for(int i = 0;i < N - B;i++) { G[i][i+1] = 0; G[i+1][i] = 0; } } else { for(int i = 0;i < N - A;i++) { G[i][i+1] = 1; G[i+1][i] = 1; } } out.println("YES"); for(int i = 0;i < N;i++) { for(int j = 0;j < N;j++) { out.print(G[i][j]); } out.println(); } } }
E問題
問題
Adilbekの家はx軸上で表される通りに存在している
通りはとても暗いので、照らすためにいくつかの照明を設置したい
通りには照明を設置するための箇所があり、それぞれに対応している
しかし、箇所にはランプが設置出来ない
それぞれパワーの違いによって異なるタイプの照明がある
位置に設置し、パワーがである照明は区間を照らす
照明はパワーがまでの異なるタイプがそれぞれ無限個売っている
客は1つのタイプしか購入できない
パワーがの照明を購入するにはのコストが掛かる
通りの全体を照らすための照明を購入する最小コストを求める
考察
各パワーについて
箇所すべて照らせるようにを照らすように貪欲に試していく。
試していく途中でがランプが設置出来ない位置の場合は
区間内(既に設置した1つ前のランプの位置と設置出来ない位置の間)でランプが設置出来る最大の位置に次のランプを設置するようにする
との制約から間に合わないと思ってしまったが、意外と間に合う
これは各パワーを試していく中でが増加していき、ランプが照らす範囲が大きくなり、ランプを置く候補が絞られていくイメージから
ソースコード
Submission #39438365 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.NoSuchElementException; public class E { int N,M,K; int[] s,a; private void solve() { N = nextInt(); M = nextInt(); K = nextInt(); s = new int[M]; for(int i = 0;i < M;i++) { s[i] = nextInt(); } a = new int[K + 1]; for(int i = 1;i <= K;i++) { a[i] = nextInt(); } boolean[] blocked = new boolean[N + 1]; for(int i = 0;i < M;i++) { blocked[s[i]] = true; } int[] notBlockIndex = new int[N + 1]; notBlockIndex[N] = N; int index = 0; for(int i = 0;i < N;i++) { if (!blocked[i]) { index = i; } notBlockIndex[i] = index; } long ans = Long.MAX_VALUE; for(int i = 1;i <= K;i++) { int now = 0; long count = 0; if (blocked[now]) { continue; } while(now < N) { int next = Math.min(N, now + i); int nextnext = notBlockIndex[next]; if (now == nextnext) { break; } now = nextnext; count++; } if (!blocked[now] && now >= N) { ans = Math.min(ans, count * a[i]); } } if (ans == Long.MAX_VALUE) { ans = -1; } out.println(ans); } }
F問題
問題
個の分岐点と本のパイプで構成されている水分配システムがある
番目のパイプは分岐点と間に接続されている
の場合はからに向かっての水が流れる
の場合はからに向かっての水が流れる
このを求めたい
番目の分岐点に水の流入量と水の放出量の差が定められている
の場合は番目の分岐点への水の流入量が水の放出量より多いことを表す
の場合は番目の分岐点からの水の放出量が水の流入量より多いことを表す
が上記の要件を満たすように出来るか
出来る場合はを出力し、不可能な場合-1を出力する
考察
要件を満たすを求められるかは各分岐点の放出量の総和と各分岐点の流出量の総和が等しく出来るかで分かる
適当な分岐点を決め、パイプで接続している分岐点に水を放出または、からへ水を流入すること考える
パイプが分岐点からに接続されている場合、
パイプが分岐点からに接続されている場合、
そしてから分の流量を加える
これを分岐点と接続されている次の分岐点間のパイプについて同様に考え、繰り返していき
すべてのパイプについて流量をDFS等で貪欲に決めていく
ソースコード
Submission #39543693 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.NoSuchElementException; public class F { int N,M; int[] x,y; long[] s,flow,sum; boolean[] visited; ArrayList<Edge>[] graph; private class Edge { int ind, to, dir; public Edge(int ind, int to, int dir) { this.ind = ind; this.to = to; this.dir = dir; } } private void dfs(int n) { visited[n] = true; for(Edge e : graph[n]) { if (visited[e.to]) { continue; } dfs(e.to); flow[e.ind] = s[e.to] * e.dir; s[n] += s[e.to]; } } @SuppressWarnings("unchecked") private void solve() { N = nextInt(); s = new long[N]; sum = new long[N]; for(int i = 0;i < N;i++) { s[i] = nextInt(); } M = nextInt(); x = new int[M]; y = new int[M]; for(int i = 0;i < M;i++) { x[i] = nextInt()-1; y[i] = nextInt()-1; } long sum = 0; for(int i = 0;i < N;i++) { sum += s[i]; } if (sum != 0) { out.println("Impossible"); return; } graph = new ArrayList[N]; for(int i = 0;i < N;i++) { graph[i] = new ArrayList<>(); } for(int i = 0;i < M;i++) { graph[x[i]].add(new Edge(i, y[i], 1)); graph[y[i]].add(new Edge(i, x[i], -1)); } flow = new long[M]; visited = new boolean[N]; dfs(0); out.println("Possible"); for(int i = 0;i < M;i++) { out.println(flow[i]); } out.println(); } }
G問題(未解決)
問題
個の頂点を含む木が与えられる
頂点には数値が書かれている
関数は頂点と頂点間のパスに含まれる頂点(を含める)に書かれている数値の最大公約数を求める
の各整数に対しての計算結果がその整数に対応しているのペア数を数えたい
考察
Codeforces #486 Div3
Editorial
A問題
考察
に含まれる整数の種類が個未満なら取り出すのは不可能
個以上の場合は
ソースコード
Submission #38839259 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.NoSuchElementException; public class A { int N,K; int[] a; private void solve() { N = nextInt(); K = nextInt(); a = new int[N]; for(int i = 0;i < N;i++) { a[i] = nextInt(); } int[] map = new int[100 + 1]; for(int i = 0;i < N;i++) { map[a[i]] = i + 1; } ArrayList<Integer> ans = new ArrayList<>(); for(int i = 0;i < 100 + 1;i++) { if (map[i] > 0) { ans.add(map[i]); } } if (ans.size() < K){ out.println("NO"); return; } out.println("YES"); for(int i = 0;i < K;i++) { if (i != 0) { out.print(" "); } out.print(ans.get(i)); } out.println(); } }
B問題
考察
文字列の長さで個の文字列をソートして
が部分文字列としてに含まれているかをについてチェック
文字列の長さでソートするのはよく考えると分かる
- の場合が部分文字列としてに含まれるのはの場合のみ
- 並べ替えが可能な場合、が部分文字列としてに必ず含まれていなければいけない
ソースコード
Submission #38842064 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.NoSuchElementException; public class B { int N; String[] s; private void sort(String[] s) { for(int i = 0;i < N;i++) { for(int j = i+1;j < N;j++) { if (s[i].length() > s[j].length()) { String tmp = s[i]; s[i] = s[j]; s[j] = tmp; } } } } private void solve() { N = nextInt(); s = new String[N]; for(int i = 0;i < N;i++) { s[i] = next(); } sort(s); for(int i = 0;i < N-1;i++) { if (s[i+1].indexOf(s[i]) == -1) { out.println("NO"); return; } } out.println("YES"); for(int i = 0;i < N;i++) { out.println(s[i]); } } }
C問題
問題
個の長さの各数列が与えられる
各数列の長さの総和は以下である
2つの数列とを選び、以下の操作が出来る
- とからそれぞれ1つずつを取り除く
操作後、数列の総和と数列の総和を等しくすることが可能か
可能な場合はとを出力する
考察
操作後の2つの数列の総和を等しくしたいので、どの数列からどの値を取り除いて数列の総和はどうなるのかをあらかじめ求めておく。(以下のようなペアを作っておく)
のペアをソートする
あとは各ペアに対してが同じでが異なるもう一つペアが存在するか二分探索
存在する場合はその2つのペアが解
ソースコード
Submission #38854484 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class C { int K; int[] N; int[][] S; ArrayList<D> list; private class D implements Comparable<D> { long sum; int ij,xy; public D(long sum,int ij,int xy) { this.sum = sum; this.ij = ij; this.xy = xy; } public int compareTo(D other) { if (this.sum != other.sum) { return Long.compare(this.sum, other.sum); } if (this.ij != other.ij) { return Integer.compare(this.ij, other.ij); } return Integer.compare(this.xy, other.xy); } public String toString() { return "[sum=" + this.sum + " ij=" + this.ij + " xy=" + this.xy + "]"; } } private void solve() { K = nextInt(); N = new int[K]; S = new int[K][]; list = new ArrayList<>(); for(int i = 0;i < K;i++) { N[i] = nextInt(); S[i] = new int[N[i]]; long sum = 0; for(int j = 0;j < N[i];j++) { S[i][j] = nextInt(); sum += S[i][j]; } for(int j = 0;j < N[i];j++) { long removedSum = sum - S[i][j]; list.add(new D(removedSum, i + 1, j+1)); } } Collections.sort(list); ArrayList<D> tmpList = new ArrayList<>(); D pre = list.get(0); for(int i = 1;i < list.size();i++) { D next = list.get(i); if (!(pre.sum == next.sum && pre.ij == next.ij)) { tmpList.add(pre); pre = next; } } tmpList.add(pre); list = tmpList; for(int i = 0;i < list.size();i++) { D d1 = list.get(i); int low = i; int high = list.size(); while(high - low > 1) { int mid = high + low >> 1; D d2 = list.get(mid); if (d1.compareTo(d2) <= 0) { high = mid; } else { low = mid; } } if (high != list.size()) { D d2 = list.get(high); if (d1.sum == d2.sum && d1.ij != d2.ij) { out.println("YES"); out.println(d1.ij + " " + d1.xy); out.println(d2.ij + " " + d2.xy); return; } } } out.println("NO"); } }
D問題
考察
よく考えたら、4つ以上の点を集合に含めることが出来ないことが分かる
例として、3つの点X,Y,Zを考える
点Xと点Yの距離を
点Yと点Zの距離を
とすると点Xと点Zの距離をの形にするにはである必要がある
ここで4つ目の点Wを加えるとする
点Zと点Wの距離を
上記のを考えるとから
点Xと点Wの距離はとなり、2の累乗の形に出来ないので、3つまで点を見れば良いことが分かる
「距離」と「点X」を決め打ちして「点Xの位置-距離」または「点Xの位置+距離」に点が存在するならば
その点を集合に追加していく
ソースコード
Submission #38969303 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.NoSuchElementException; public class D { int N; ArrayList<Integer> x; private void solve() { N = nextInt(); x = new ArrayList<>(); for(int i = 0;i < N;i++) { x.add(nextInt()); } Collections.sort(x); int[] ans = new int[3]; int size = 0; for(int v : x) { for(int bit = 0;bit < 31;bit++) { int small = v - (1 << bit); int big = v + (1 << bit); int ind1 = Collections.binarySearch(x, small); int ind2 = Collections.binarySearch(x, big); if (ind1 >= 0 && ind2 >= 0 && size < 3) { ans[0] = small; ans[1] = v; ans[2] = big; size = 3; } if (ind1 >= 0 && ind2 < 0 && size < 2) { ans[0] = small; ans[1] = v; ans[2] = -1; size = 2; } if (ind1 < 0 && ind2 >= 0 && size < 2) { ans[0] = v; ans[1] = big; ans[2] = -1; size = 2; } } } if (size == 0) { out.println(1); out.println(x.get(0)); return; } out.println(size); for(int i = 0;i < size;i++) { if (i != 0) { out.print(" "); } out.print(ans[i]); } out.println(); } }
E問題
問題
整数が与えられる
の桁目と桁目を交換する操作(交換操作)が出来る(交換操作後に先頭から0が1文字以上続く整数にするような交換操作は出来ない)
整数が25で割り切れる数になるように交換操作を繰り返した場合の交換操作回数の最小値を求めたい
25で割り切れる数に出来ない場合は-1を出力する
考察
で割り切れる数の下2桁に注目すると
の繰り返しになること気付くので、下2桁をその4パターンに絞る
まず、下2桁がになる場合について考える
交換操作回数を最小にしたいので、下1桁目から一番近いを持つ桁(下1桁目も含む)からを交換操作をしながら下1桁目に配置する
下2桁目も同様にを持つ一番近い桁(下1桁目は除く)から交換操作をしながら下2桁目に配置する
その後、先頭1桁目がの場合はではない値を持つ桁から、先頭に値を交換操作を行いながら配置する
下2桁がになるような最小の交換操作回数は
も同じように実際に交換操作を行いながら、交換操作回数の最小値を求める
ソースコード
Submission #39022664 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.NoSuchElementException; public class E { char[] N; private void swapFromLToR(int l,int r,char[] ch) { for(int k = l;k < r;k++) { char tmp = ch[k]; ch[k] = ch[k+1]; ch[k+1] = tmp; } } private void swapFromRToL(int l,int r,char[] ch) { for(int k = r;k > l;k--) { char tmp = ch[k]; ch[k] = ch[k-1]; ch[k-1] = tmp; } } private long toLong(char[] ch) { long ret = 0L; for(char c : ch) { ret *= 10; ret += c - '0'; } return ret; } private int calc(char[] ch,char a,char b) { int ret = 0; int r = -1; for(int i = ch.length-1;i >= 0;i--) { if (ch[i] == b) { r = i; break; } } if (r == -1) { return Integer.MAX_VALUE; } swapFromLToR(r,ch.length-1 ,ch); ret += ch.length-1 - r; int l = -1; for(int i = ch.length-2;i >= 0;i--) { if (ch[i] == a) { l = i; break; } } if (l == -1) { return Integer.MAX_VALUE; } swapFromLToR(l, ch.length-2,ch); ret += ch.length-2 - l; if (ch[0] == '0') { int z = -1; for(int i = 1;i < ch.length;i++) { if (ch[i] != '0') { z = i; break; } } if (z == -1) { return Integer.MAX_VALUE; } swapFromRToL(z, 0, ch); ret += z; } long n = toLong(ch); if (n % 25 == 0) { return ret; } return Integer.MAX_VALUE; } private void solve() { N = next().toCharArray(); int ans = calc(Arrays.copyOf(N,N.length),'2','5'); ans = Math.min(ans,calc(Arrays.copyOf(N,N.length),'5', '0')); ans = Math.min(ans,calc(Arrays.copyOf(N,N.length),'7', '5')); ans = Math.min(ans,calc(Arrays.copyOf(N,N.length),'0', '0')); if (ans == Integer.MAX_VALUE) { out.println(-1); } else { out.println(ans); } } }
F問題
問題
数直線上でPolycarpはからに移動したい(左から右への移動のみ可能)
個の雨が降っている区間が与えられ、(この区間同士は重ならない)
さらに個の傘の位置、重さが与えられる
Polycarpは初めにから出発する時は傘を持っていない
Polycarpはからに移動する間、傘を拾ったり、捨てることができ、傘はいくつでも拾うことが出来る
Polycarpは雨で濡れたくないので、雨が降っている区間を移動している間は少なくとも1つ以上の傘を持っている必要がある
雨が降っていない区間では傘を持っている必要は無く、雨が降っている区間内で傘を交換出来る
1単位移動するごとに持っている傘の重さの総和の分だけ疲労が増加する
からに移動する際、出来るだけ疲労が少ないようにしたいので、
疲労の最小値を求める
からに移動が出来ない場合、-1を出力する
考察
傘を持っていない状態をとしてDPをする
基本的にはのように1単位ずつ移動することを考えると、複数の傘を同時に持つのは最適ではないことが分かり
どの傘をの時に持っていればよいかをさらに考えるとDPでまとめられるかという発想になりそう
実装に関しては最初メモ化再帰で実装していたがなぜかWAが取れず、結局forDPに直して通した
ソースコード
Submission #39193925 - Codeforces
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.NoSuchElementException; public class F { private static final long INF = (long)1e17+7; int A,N,M; int[] U; long[] P; boolean[] R; long[][] dp; private void solve() { A = nextInt(); N = nextInt(); M = nextInt(); R = new boolean[A + 1]; for(int i = 0;i < N;i++) { int l = nextInt(); int r = nextInt(); for(int j = l;j < r;j++) { R[j] = true; } } U = new int[A + 1]; Arrays.fill(U, -1); P = new long[M]; Arrays.fill(P, INF); for(int i = 0;i < M;i++) { int x = nextInt(); P[i] = nextInt(); if (U[x] == -1 || P[U[x]] >= P[i]) { U[x] = i; } } dp = new long[A + 1][M + 1]; for(int i = 0;i < A + 1;i++) { Arrays.fill(dp[i], INF); } dp[0][M] = 0; for(int i = 0;i < A;i++) { for(int j = 0;j < M + 1;j++) { if (dp[i][j] == INF) { continue; } if (!R[i]) { dp[i + 1][M] = Math.min(dp[i + 1][M],dp[i][j]); } if (j < M) { dp[i + 1][j] = Math.min(dp[i + 1][j], dp[i][j] + P[j]); } if (U[i] != -1) { dp[i + 1][U[i]] = Math.min(dp[i + 1][U[i]], dp[i][j] + P[U[i]]); } } } long ans = INF; for(int i = 0;i < M + 1;i++) { ans = Math.min(ans, dp[A][i]); } if (ans == INF) { out.println(-1); } else { out.println(ans); } } }
yukicoder long contest 1 stick xor
考察
愚直にやって終わった感じでした。
愚直にやる以外は全然思いつかなかった。
1ターン毎に現在の盤面から最も多く漆黒→純白に反転できる操作を盤面に適用していく。
操作対象となるセル(x,y)のx,yはランダムに決定した。
・xは1~N-L,yはランダム(範囲1~N-L)
・yは1~N-L,xはランダム(範囲1~N-L)
あとは単純に漆黒→純白に反転できる数が最も多い操作を選んで盤面に適用して、次のターンで繰り返し...
ソースコード
最終サブミット
https://yukicoder.me/submissions/260603
# coding: utf-8 import random from heapq import heappush,heappop #操作opを盤面gridに適用する def operation_grid(op, grid): rev_grid = grid #縦方向の操作 if op[1] == op[3]: length = op[2] - op[0] + 1 for i in range(length): rev_grid[op[0]-1+i][op[1]-1] = (grid[op[0]-1+i][op[1]-1]+1)&1 #横方向の操作 if op[0] == op[2]: length = op[3] - op[1] + 1 for i in range(length): rev_grid[op[0]-1][op[1]-1+i] = (grid[op[0]-1][op[1]-1+i]+1)&1 return rev_grid #(x,y)=(base_x,base_y) #(x,y)のセルから横方向に幅length高さ1の範囲の漆黒のセル数をカウント def count_black_horizon(grid, base_x, base_y, length): return grid[base_y][base_x:base_x + length].count(1) #(x,y)=(base_x,base_y) #(x,y)のセルから縦方向に幅1高さlengthの範囲の漆黒のセル数をカウント def count_black_vertical(grid, base_x, base_y, length): return [grid[i][base_x] for i in range(base_y,base_y + length)].count(1) N,K = map(int,input().split()) L = list(map(int,input().split())) A = [list(map(int,list(input()))) for i in range(N)] #出力する操作 operation = [] #適当な値 P = 3 for k in range(K): #(-漆黒から純白への反転数, [出力する操作]) hq = [] #N-L[k]=盤面からはみ出ない最上、最左 for x in range(N-L[k]): for i in range(P): #yはランダム値(0~N-L[k]) y = random.randint(0, N-L[k]) #(x,y)のセルから漆黒から純白に反転する数を求める horizon_cnt = count_black_horizon(A, x, y, L[k]) vertical_cnt = count_black_vertical(A, x, y, L[k]) if horizon_cnt < vertical_cnt: heappush(hq, (-vertical_cnt, [y+1, x+1, y+L[k], x+1])) else: heappush(hq, (-horizon_cnt, [y+1, x+1, y+1, x+L[k]])) for y in range(N-L[k]): for i in range(P): #xはランダム値(0~N-L[k]) x = random.randint(0, N-L[k]) #(x,y)のセルから漆黒から純白に反転する数を求める horizon_cnt = count_black_horizon(A, x, y, L[k]) vertical_cnt = count_black_vertical(A, x, y, L[k]) if horizon_cnt < vertical_cnt: heappush(hq, (-vertical_cnt, [y+1, x+1, y+L[k], x+1])) else: heappush(hq, (-horizon_cnt, [y+1, x+1, y+1, x+L[k]])) #漆黒から純白へ最も多く反転できる操作を取り出す cnt, op = heappop(hq) #操作を盤面に適用 A = operation_grid(op, A) operation.append(op) print('\n'.join([' '.join(list(map(str,op))) for op in operation]))
反省
愚直のその先を全然考察出来なかった。
マラソンっぽい思考難しい
学びの参考にしたい
Good Bye 2017 C New Year and Curling
問題
半径rの円の中心のx座標がN個与えられる。
i = 0,1,2,3...N-1の順に円はy=10^100からy = 0に向かって進む。
他の円に接した時、y = 0に円が接した時に円は止まる。
最終的なN個の円のy座標を求める。
考察
i番目の円aをy=0に向かって進める際に、既に止まっているj番目(j < i)の円bを考える。
abs(x[i] - x[j])がr * 2(円aの半径+円bの半径)より大きい場合はその円同士は接することはない。
abs(x[i] - x[j])がr * 2以下の場合は接するので、円aと円bの距離は必ずr * 2になる。
後は三平方の定理からsqrt( (r * 2) * (r * 2) - abs(x[i] - x[j]) * abs(x[i] - x[j]) )が円aと円bのy座標の距離となる。
y[j] + (円aと円bのy座標の距離)の最大値が円aの最終的なy座標となる。
ソースコード
戒め
これは色々良くないコードで
y座標が大きいものから取って、円が止まるy座標を二分探索してる
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class C { int N,r; int[] x; ArrayList<Point> points; private class Point implements Comparable<Point>{ double y,x; public Point(double y,double x) { this.y = y; this.x = x; } public int compareTo(Point point) { return Double.compare(point.y,this.y); } } private double dis(Point p,double y,double x) { return (p.y - y) * (p.y - y) + (p.x - x) * (p.x - x); } private boolean isHit(double y,double x) { for(Point p : points) { if (dis(p,y,x) <= (r * 2) * (r * 2)) { return true; } } return false; } private double getY(PriorityQueue<Point> q,double x) { double lowY = r; while(q.size() > 0) { Point p = q.poll(); if (isHit(p.y,x)) { lowY = Math.max(lowY,p.y); } } double low = lowY; double high = 10000000; for(int j = 0;j < 100;j++) { double mid = (high + low) / 2; if (isHit(mid,x)) { low = mid; } else { high = mid; } } return high; } private void solve() { N = nextInt(); r = nextInt(); x = new int[N]; for(int i = 0;i < N;i++) { x[i] = nextInt(); } PriorityQueue<Point> pq = new PriorityQueue<>(); points = new ArrayList<>(); for(int i = 0;i < N;i++) { PriorityQueue<Point> tmp = new PriorityQueue<>(); tmp.addAll(pq); double minY = getY(tmp,x[i]); pq.add(new Point(minY,x[i])); points.add(new Point(minY,x[i])); } for(int i = 0;i < points.size();i++) { if (i != 0) { out.print(" "); } out.print(String.format("%.09f",points.get(i).y)); } out.println(); } public static void main(String[] args) { out.flush(); new C().solve(); out.close(); } /* Input */ private static final InputStream in = System.in; private static final PrintWriter out = new PrintWriter(System.out); private final byte[] buffer = new byte[2048]; private int p = 0; private int buflen = 0; private boolean hasNextByte() { if (p < buflen) return true; p = 0; try { buflen = in.read(buffer); } catch (IOException e) { e.printStackTrace(); } if (buflen <= 0) return false; return true; } public boolean hasNext() { while (hasNextByte() && !isPrint(buffer[p])) { p++; } return hasNextByte(); } private boolean isPrint(int ch) { if (ch >= '!' && ch <= '~') return true; return false; } private int nextByte() { if (!hasNextByte()) return -1; return buffer[p++]; } public String next() { if (!hasNext()) throw new NoSuchElementException(); StringBuilder sb = new StringBuilder(); int b = -1; while (isPrint((b = nextByte()))) { sb.appendCodePoint(b); } return sb.toString(); } public int nextInt() { return Integer.parseInt(next()); } public long nextLong() { return Long.parseLong(next()); } public double nextDouble() { return Double.parseDouble(next()); } }
これは想定解で解き直したやつ(スッキリ)
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class C { int N,r; int[] x; double[] y; private void solve() { N = nextInt(); r = nextInt(); x = new int[N]; y = new double[N]; for(int i = 0;i < N;i++) { x[i] = nextInt(); } for(int i = 0;i < N;i++) { double maxY = r; for(int j = 0;j < i;j++) { int distanceX = Math.abs(x[i] - x[j]); if (distanceX <= r * 2) { maxY = Math.max(maxY,y[j] + Math.sqrt((r * 2) * (r * 2) - distanceX * distanceX)); } } y[i] = maxY; } for(int i = 0;i < N;i++) { if (i != 0) { out.print(" "); } out.print(String.format("%.09f",y[i])); } out.println(); } public static void main(String[] args) { out.flush(); new C().solve(); out.close(); } }
Educational Codeforces Round 35 (Rated for Div. 2) C Three Garlands
問題
整数k_i(1<= i <= 3)が与えられる。
x_i,x_i + k_i,x_i + 2*k_i,x_i + 3*k_i...の値を選択できる。
整数x_1,x_2,x_3の値を定めた時、max(x_1,x_2,x_3)以上の整数すべてを選択できるかを判定する。
考察
・k_i = 1が1つ以上存在するのならYES
値の差が1なので整数すべてを覆える
・k_i = 2が2つ以上存在するのならYES
x_iが偶数、奇数を含むようにすればOK
・k_i = 3が3つ存在するのならYES
x_i mod 3 = 0
x_i mod 3 = 1
x_i mod 3 = 2
となるx_iを含むようにすればOK
・k_i = 2が1つ、k_i = 4が2つ存在するのならYES
x_i mod 2 = 0
x_i mod 4 = 1
x_i mod 4 = 3
となるx_iを含むようにすればOK
それ以外はNO
ソースコード
import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.*; public class C { int k1,k2,k3; private void solve() { k1 = nextInt(); k2 = nextInt(); k3 = nextInt(); int[] count = new int[1500 + 1]; count[k1]++; count[k2]++; count[k3]++; if (count[1] >= 1 || count[2] >= 2 || count[3] >= 3 || (count[2] == 1 && count[4] == 2)) { out.println("YES"); } else { out.println("NO"); } } public static void main(String[] args) { out.flush(); new C().solve(); out.close(); } /* Input */ private static final InputStream in = System.in; private static final PrintWriter out = new PrintWriter(System.out); private final byte[] buffer = new byte[2048]; private int p = 0; private int buflen = 0; private boolean hasNextByte() { if (p < buflen) return true; p = 0; try { buflen = in.read(buffer); } catch (IOException e) { e.printStackTrace(); } if (buflen <= 0) return false; return true; } public boolean hasNext() { while (hasNextByte() && !isPrint(buffer[p])) { p++; } return hasNextByte(); } private boolean isPrint(int ch) { if (ch >= '!' && ch <= '~') return true; return false; } private int nextByte() { if (!hasNextByte()) return -1; return buffer[p++]; } public String next() { if (!hasNext()) throw new NoSuchElementException(); StringBuilder sb = new StringBuilder(); int b = -1; while (isPrint((b = nextByte()))) { sb.appendCodePoint(b); } return sb.toString(); } public int nextInt() { return Integer.parseInt(next()); } public long nextLong() { return Long.parseLong(next()); } public double nextDouble() { return Double.parseDouble(next()); } }