🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

920閲覧

AtCoderのabc001_4で、二次元配列の重複を削除したい

smile_20200722

総合スコア11

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/03/30 07:51

編集2021/04/01 01:55

AtCoderのabc001_4でつまづいています。

具体的には、途中の工程の二次元配列の処理の仕方で、重複する値を削除した配列を表示したいです。
実際の問題では以下の部分です。
「2つ以上のメモに書かれていた感雨時刻が重複した場合、1つの連続した雨とみなす。」

ソースコードは、AtCoderの問題の入力と出力とは若干違うものになっています。

https://atcoder.jp/contests/abc001/tasks/abc001_4

my_delete関数に、値の入った二次元配列と空の二次元配列を渡して、空の二次元配列に重複を削除した値を入れたいです。
ソースコードの空の二次元配列の渡し方も、これだと「0-0」が残ってしまうと思っています。
もっとスマートなやり方ありましたら違う方法でもかまいません。
どのようにループを回せばいいのか、添字を書けばいいのか、からちょっとわからない状況です。
どうぞよろしくお願いいたします。

該当のソースコード

c++

1#include <bits/stdc++.h> 2using namespace std; 3 4void my_delete(vector<vector<int>> &T1, vector<vector<int>> &T2) { 5 T2.at(0).at(0) = T1.at(0).at(0); 6 for(int i = 0; i < T1.size(); i++) { 7 } 8} 9 10void my_sort(vector<vector<int>> &T) { 11 for(int i = 0; i < T.size() - 1; i++) { 12 for(int j = T.size() - 1; j > i; j--) { 13 if(T.at(j).at(0) < T.at(j - 1).at(0)) { 14 int tmp = T.at(j).at(0); 15 T.at(j).at(0) = T.at(j - 1).at(0); 16 T.at(j - 1).at(0) = tmp; 17 tmp = T.at(j).at(1); 18 T.at(j).at(1) = T.at(j - 1).at(1); 19 T.at(j - 1).at(1) = tmp; 20 } 21 } 22 } 23} 24 25int my_round(int x, int m) { 26 string s = to_string(x); 27 if(s.at(3) == '0') { 28 return atoi(s.c_str()); 29 } 30 31 if(m == 0) { 32 s.at(3) = '0'; 33 } else { 34 s.at(3) = '5'; 35 } 36 return atoi(s.c_str()); 37} 38 39int main() { 40 int N = 3; 41 vector<string> SE = {"1106-1123", "1129-1203", "1148-1210"}; 42 43 for(int i = 0; i < N; i++) { 44 cout << SE.at(i) << endl; 45 } 46 vector<vector<int>> T(N, vector<int>(2)); 47 for(int i = 0; i < N; i++) { 48 string tmp; 49 tmp = SE.at(i).at(0); 50 tmp += SE.at(i).at(1); 51 tmp += SE.at(i).at(2); 52 tmp += SE.at(i).at(3); 53 T.at(i).at(0) = atoi(tmp.c_str()); 54 T.at(i).at(0) = my_round(T.at(i).at(0), 0); 55 56 tmp = SE.at(i).at(5); 57 tmp += SE.at(i).at(6); 58 tmp += SE.at(i).at(7); 59 tmp += SE.at(i).at(8); 60 T.at(i).at(1) = atoi(tmp.c_str()); 61 T.at(i).at(1) = my_round(T.at(i).at(1), 5); 62 } 63 64 my_sort(T); 65 66 cout << "output:" << endl; 67 for(int i = 0; i < N; i++) { 68 cout << T.at(i).at(0) << '-' << T.at(i).at(1) << endl; 69 } 70 71 vector<vector<int>> ans(N, vector<int>(2)); 72 my_delete(T, ans); 73 74 cout << "ans:" << endl; 75 for(int i = 0; i < N; i++) { 76 cout << ans.at(i).at(0) << '-' << ans.at(i).at(1) << endl; 77 } 78 79 return 0; 80}

期待する出力

数値を時刻として、続いている間の時刻を一つにまとめて表示したいです。

1106-1123 1129-1203 1148-1210 output: 1100-1125 1120-1205 1140-1210 ans: 1100-1210

丸めの関数を修正したコード

AtCoderの問題の丸め方が間違えているとのご指摘を受けて、自分で考えた関数です。

c++

1string my_round_start(string s) { 2 if(s.at(3) == '0' || s.at(3) == '1' || s.at(3) == '2' || s.at(3) == '3' || 3 s.at(3) == '4') { 4 s.at(3) = '0'; 5 } else if(s.at(3) == '5' || s.at(3) == '6' || s.at(3) == '7' || 6 s.at(3) == '8' || s.at(3) == '9') { 7 s.at(3) = '5'; 8 } 9 return s; 10} 11 12string my_round_end(string s) { 13 if(s.at(3) == '0') { 14 s.at(3) = '0'; 15 } else if(s.at(3) == '1' || s.at(3) == '2' || s.at(3) == '3' || 16 s.at(3) == '4' || s.at(3) == '5') { 17 s.at(3) = '5'; 18 } else if(s.at(3) == '6' || s.at(3) == '7' || s.at(3) == '8') { 19 if(s.at(2) == '0') { 20 s.at(2) = '1'; 21 s.at(3) = '0'; 22 } else if(s.at(2) == '1') { 23 s.at(2) = '2'; 24 s.at(3) = '0'; 25 } else if(s.at(2) == '2') { 26 s.at(2) = '3'; 27 s.at(3) = '0'; 28 } else if(s.at(2) == '3') { 29 s.at(2) = '4'; 30 s.at(3) = '0'; 31 } else if(s.at(2) == '4') { 32 s.at(2) = '5'; 33 s.at(3) = '0'; 34 } else if(s.at(2) == '5') { 35 if(s.at(0) == '0' && s.at(1) == '0') { 36 s = "0100"; 37 } else if(s.at(0) == '0' && s.at(1) == '1') { 38 s = "0200"; 39 } else if(s.at(0) == '0' && s.at(1) == '2') { 40 s = "0300"; 41 } else if(s.at(0) == '0' && s.at(1) == '3') { 42 s = "0400"; 43 } else if(s.at(0) == '0' && s.at(1) == '4') { 44 s = "0500"; 45 } else if(s.at(0) == '0' && s.at(1) == '5') { 46 s = "0600"; 47 } else if(s.at(0) == '0' && s.at(1) == '6') { 48 s = "0700"; 49 } else if(s.at(0) == '0' && s.at(1) == '7') { 50 s = "0800"; 51 } else if(s.at(0) == '0' && s.at(1) == '8') { 52 s = "0900"; 53 } else if(s.at(0) == '0' && s.at(1) == '9') { 54 s = "1000"; 55 } else if(s.at(0) == '1' && s.at(1) == '0') { 56 s = "1100"; 57 } else if(s.at(0) == '1' && s.at(1) == '1') { 58 s = "1200"; 59 } else if(s.at(0) == '1' && s.at(1) == '2') { 60 s = "1300"; 61 } else if(s.at(0) == '1' && s.at(1) == '3') { 62 s = "1400"; 63 } else if(s.at(0) == '1' && s.at(1) == '4') { 64 s = "1500"; 65 } else if(s.at(0) == '1' && s.at(1) == '5') { 66 s = "1600"; 67 } else if(s.at(0) == '1' && s.at(1) == '6') { 68 s = "1700"; 69 } else if(s.at(0) == '1' && s.at(1) == '7') { 70 s = "1800"; 71 } else if(s.at(0) == '1' && s.at(1) == '8') { 72 s = "1900"; 73 } else if(s.at(0) == '1' && s.at(1) == '9') { 74 s = "2000"; 75 } else if(s.at(0) == '2' && s.at(1) == '0') { 76 s = "2100"; 77 } else if(s.at(0) == '2' && s.at(1) == '1') { 78 s = "2200"; 79 } else if(s.at(0) == '2' && s.at(1) == '2') { 80 s = "2300"; 81 } else if(s.at(0) == '2' && s.at(1) == '3') { 82 s = "2400"; 83 } 84 } 85 } else if(s.at(3) == '9') { 86 if(s.at(2) == '0') { 87 s.at(2) = '1'; 88 s.at(3) = '0'; 89 } else if(s.at(2) == '1') { 90 s.at(2) = '2'; 91 s.at(3) = '0'; 92 } else if(s.at(2) == '2') { 93 s.at(2) = '3'; 94 s.at(3) = '0'; 95 } else if(s.at(2) == '3') { 96 s.at(2) = '4'; 97 s.at(3) = '0'; 98 } else if(s.at(2) == '4') { 99 s.at(2) = '5'; 100 s.at(3) = '0'; 101 } else if(s.at(2) == '5') { 102 if(s.at(0) == '0' && s.at(1) == '0') { 103 s = "0100"; 104 } else if(s.at(0) == '0' && s.at(1) == '1') { 105 s = "0200"; 106 } else if(s.at(0) == '0' && s.at(1) == '2') { 107 s = "0300"; 108 } else if(s.at(0) == '0' && s.at(1) == '3') { 109 s = "0400"; 110 } else if(s.at(0) == '0' && s.at(1) == '4') { 111 s = "0500"; 112 } else if(s.at(0) == '0' && s.at(1) == '5') { 113 s = "0600"; 114 } else if(s.at(0) == '0' && s.at(1) == '6') { 115 s = "0700"; 116 } else if(s.at(0) == '0' && s.at(1) == '7') { 117 s = "0800"; 118 } else if(s.at(0) == '0' && s.at(1) == '8') { 119 s = "0900"; 120 } else if(s.at(0) == '0' && s.at(1) == '9') { 121 s = "1000"; 122 } else if(s.at(0) == '1' && s.at(1) == '0') { 123 s = "1100"; 124 } else if(s.at(0) == '1' && s.at(1) == '1') { 125 s = "1200"; 126 } else if(s.at(0) == '1' && s.at(1) == '2') { 127 s = "1300"; 128 } else if(s.at(0) == '1' && s.at(1) == '3') { 129 s = "1400"; 130 } else if(s.at(0) == '1' && s.at(1) == '4') { 131 s = "1500"; 132 } else if(s.at(0) == '1' && s.at(1) == '5') { 133 s = "1600"; 134 } else if(s.at(0) == '1' && s.at(1) == '6') { 135 s = "1700"; 136 } else if(s.at(0) == '1' && s.at(1) == '7') { 137 s = "1800"; 138 } else if(s.at(0) == '1' && s.at(1) == '8') { 139 s = "1900"; 140 } else if(s.at(0) == '1' && s.at(1) == '9') { 141 s = "2000"; 142 } else if(s.at(0) == '2' && s.at(1) == '0') { 143 s = "2100"; 144 } else if(s.at(0) == '2' && s.at(1) == '1') { 145 s = "2200"; 146 } else if(s.at(0) == '2' && s.at(1) == '2') { 147 s = "2300"; 148 } else if(s.at(0) == '2' && s.at(1) == '3') { 149 s = "2400"; 150 } 151 } 152 } 153 return s; 154}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuma-s

2021/03/30 09:33

期待する出力を質問に追記してください。 丸め方が AtCoderの問題に従っていません。
smile_20200722

2021/03/30 09:43

kazuma-sさん、返信ありがとうございます。 round_n関数に } else if (m == 5) { を追記しました。 こちらで実行エラーが出なかったのでそのままにしてしまいました。 それともAtCoderの問題の「丸め」の理解が間違えてるということでしょうか?
smile_20200722

2021/03/30 09:50

kazuma-sさん、期待する出力を追記させていただきました。 output:以下はAtCoderの問題に従って丸めたつもりなのですが、出力例を見て判断したので、 自分としての理解があやしいです。 出力の数値は、AtCoderの問題にあるものです。 よろしくお願いいたします。
kazuma-s

2021/03/30 09:56

「雨の降り始め・降り終わりはそれぞれ直前・直後の 5分単位の時刻に丸める。」だから、 1106-1123 は 1105-1125 に、1129-1203 は 1125-1205 にならないといけません。 もし、1129-1159 だったら、1125-1200 にならないといけません。
kazuma-s

2021/03/30 17:00 編集

> 出力の数値は、AtCoderの問題にあるものです。 これですね。 「丸めた後の結果において、2つ以上のメモに書かれていた感雨時刻が重複した 場合、1つの連続した雨とみなす。例えば、11:06に降り始めて 11:23にやんだ雨、 11:29に降り始めて 12:03にやんだ雨、11:48に降り始めて 12:10にやんだ雨の 3つがあった場合、11:05~11:25、11:25~12:05、11:45~12:10の3つの雨であるが、 時間がかぶっているところをくっつけて 11:05から 12:10まで降っていた、 1つの連続した雨ということにする。」
smile_20200722

2021/03/31 14:15

kazuma-sさん、返信ありがとうございます。 条件文だらけの丸める関数を書いている途中でした。 今日はあまり考える時間がとれなかったので、一応それが形になるまで考えてみたいと思います。 下のソースもありがとうございます。 後ほど拝見させていただきます。
guest

回答1

0

ベストアンサー

「雨の降り始め・降り終わりはそれぞれ直前・直後の 5分単位の時刻に丸める」だから、
雨の降り始めの時刻を直前の 5分単位の時刻に丸めるとき、
末尾の 0~4 は 0 に、5~9 は 5 にするだけでよいでしょう。

雨の降り終わりの時刻を直後の 5分単位の時刻に丸めるとき、
末尾の 0 はそのまま、 1~5 は 5 にするだけでよいのですが、
6~9 は 0 にし、さらに上の桁への繰り上がりがあります。
10の位が 0~4 の場合は、1~5 にするだけでよいのですが、
10の位が 5 の場合は、0 にして、さらに上のの桁への繰り上がりがあります。
そこが 9 の場合はさらにその上の桁への繰り上がりがあります。
1956 は 2000 にしないといけないということです。

こんな複雑な丸めは面倒です。
そこで、時刻を 00:00 から m分後という値にしておけば
(m + 4) / 5 * 5 で 5分単位の時刻に切り上げることが簡単にできます。

次のコードを参考にして、理解できませんか?

C++

1#include <iostream> // cout 2#include <iomanip> // setfill, setw 3#include <string> // string 4#include <vector> // vector 5#include <algorithm> // sort 6using namespace std; 7 8struct SE { int s, e; }; // 開始時刻と終了時刻(00:00 からの分) 9 10int d2val(const char *s) // 2桁の数字の値 11{ 12 return (s[0]-'0') * 10 + (s[1]-'0'); 13} 14 15int get_time(const char *s, int round) // 時刻を分に変換して丸め 16{ 17 return (d2val(s)*60 + d2val(s+2) + round) / 5 * 5; 18} 19 20void print(int m) // 分を時刻に変換して表示 21{ 22 cout << setfill('0') << setw(2) << m/60 << setw(2) << m%60; 23} 24 25int main() 26{ 27 int n = 3; 28 string str[] = { "1148-1210" , "1106-1123", "1129-1203" }; 29 vector<SE> t(n); 30 for (int i = 0; i < n; i++) { 31 cout << str[i] << endl; 32 t[i].s = get_time(str[i].c_str(), 0); 33 t[i].e = get_time(str[i].c_str() + 5, 4); 34 } 35 36 sort(t.begin(), t.end(), 37 [](const SE& a, const SE& b) { return a.s < b.s; }); 38 39 cout << "output:\n"; 40 for (int i = 0; i < n; i++) 41 print(t[i].s), cout << '-', print(t[i].e), cout << endl; 42 43 cout << "ans:\n"; 44 for (int i = 0; i < n; ) { 45 SE se = t[i]; 46 while (++i < n && se.e >= t[i].s) // 雨のやむ時刻の検索 47 if (se.e < t[i].e) se.e = t[i].e; 48 print(se.s), cout << '-', print(se.e), cout << endl; 49 } 50}

感雨時刻の重複は、終了時刻を更新していくことで解決できるでしょう。

追記
質問に「丸めの関数を修正したコード」が追記されましたが、
無駄な記述がたくさんあります。
} else if(s.at(3) == '6' || s.at(3) == '7' || s.at(3) == '8') {
の場合の処理と、
} else if(s.at(3) == '9') { の場合の処理が全く同じです。
なぜ、
} else if(s.at(3) >= '6' && s.at(3) <= '9') { と一つにまとめないのでしょうか?

私なら次のように書きます。

C++

1#include <iostream> 2#include <string> 3using namespace std; 4 5string my_round_start(string s) 6{ 7 s.at(3) = (s.at(3) < '5') ? '0' : '5'; 8 return s; 9} 10 11string my_round_end(string s) 12{ 13 if (s.at(3) > '5') { // '6' <= s.at(3) <= '9' 14 s.at(3) = '0'; 15 if (++s.at(2) == '6') { 16 s.at(2) = '0'; 17 if (++s.at(1) > '9') { 18 s.at(1) = '0'; 19 ++s.at(0); 20 } 21 } 22 } 23 else if (s.at(3) != '0') // '1' <= s.at(3) <= '5' 24 s.at(3) = '5'; 25 return s; 26} 27 28int main() 29{ 30 string str[] = { "1106-1123", "1129-1203", "1148-1210", "1904-1956" }; 31 for (int i = 0; i < 4; i++) { 32 cout << str[i] << endl; 33 cout << my_round_start(str[i].substr(0, 4)) << '-' 34 << my_round_end(str[i].substr(5, 9)) << endl << endl; 35 } 36}

投稿2021/03/30 18:15

編集2021/04/01 04:08
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

smile_20200722

2021/04/01 08:31

kazuma-sさん、返信ありがとうございます。 追記の丸めの関数は、文字型に不等式や加算、減算などができると知りませんでした。 d2val関数やget_time関数も実際に値の流れを確認しました。 get_time関数のような数式は、経験からこのような書き方が出てくるのでしょうか? 「雨のやむ時刻の検索」についても、比較やループの仕方がまったくわからなかったので、このような書き方ができるのかと勉強になりました。
kazuma-s

2021/04/01 09:29

ひょっとして、s[3] と書けることを知らずに s.at(3) と書いていたんでしょうか?
smile_20200722

2021/04/01 14:01

そこは知っていましたが、文字コード(?)を演算できるということを知りませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問