ナソニックプログラミングコンテスト2020题解
A - Kth Term
実行時間制限: 2 sec / メモリ制限: 1024 MB
配点 : 100点
問題文
次の長さ32の数列のK番目の項を出力してください。
1, 1, 1, 2, 1, 2, 1, 5, 2, 2, 1, 5, 1, 2, 1, 14, 1, 5, 1, 5, 2, 2, 1, 15, 2, 2, 5, 4, 1, 4, 1, 51
制約
- 1 ≤ K ≤ 32 1 \leq K \leq 32 1≤K≤32
- 入力は全て整数である。
入力
入力は以下の形式で標準入力から与えられる。
K
出力
K 番目の項を出力せよ。
入力例 1
6
出力例 1
2
6番目の項は 2です。
入力例 2
27
出力例 2
5
27番目の項は 5です。
解説
没什么好说的, 从未见过这么简单的题目
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int main(){
int res[32] ={1, 1, 1, 2, 1, 2, 1, 5, 2, 2, 1, 5, 1, 2, 1, 14, 1, 5, 1, 5, 2, 2, 1, 15, 2, 2, 5, 4, 1, 4, 1, 51};
int n;
cin >> n;
cout << res[n-1]<<endl;
return 0;
}
B - Bishop
実行時間制限: 2 sec / メモリ制限: 1024 MB
配点 : 200点
問題文
縦 Hマス、横 Wマスの盤面があります。 この盤面の左上隅のマスに角行の駒が置かれています。 駒が 0回以上の好きな回数の移動を繰り返して到達できるマス目は何個あるでしょうか?
ただし、角行の駒は斜めに動くものとします。 より厳密には、駒が上から r1番目、左から c1 番目のマスから上から r2 番目、左から c2 番目のマス目に動ける条件は
- r 1 + c 1 = r 2 + c 2 r1+ c1 = r2 + c2 r1+c1=r2+c2
- r 1 − c 1 = r 2 − c 2 r1- c1 = r2 - c2 r1−c1=r2−c2
のうちちょうど一方が成立することです。たとえば、駒が図の位置にあるとき、一回で移動できる場所は赤くなっているマスです。
制約
- 1 ≤ H , W ≤ 1 0 9 1\leq H, W\leq 10^9 1≤H,W≤109
入力
入力は以下の形式で標準入力から与えられる。
H W
出力
駒が到達できるマス目の個数を出力せよ。
入力例1
4 5
出力例1
10
下図の水色のマスに到達可能です。
入力例2
7 3
出力例2
11
下図の水色のマスに到達可能です。
入力例2
1000000000 1000000000
出力例2
500000000000000000
解法
首先由于这题的运行时间限制为2s,所以这题不能用遍历的方法来解题,我们可以通过看sample的图看出来一般来说棋子可以到达棋盘整体的一半。并且在棋盘的格子数是奇数的时候需要+1。
当然这题有个非常容易发生WA的地方,当棋盘的H或者W有一个为1的时候,棋子是无法移动的,也就是说这时候的答案是1。只要避开这点就好了。
#include <iostream>
using namespace std;
typedef long long ll;
int main()
{
ll h, w;
cin >> h >> w;
if (h == 1 || w == 1) cout << 1 << endl;
else cout << (h * w + 1) / 2 << endl;
return 0;
}
C - Sqrt Inequality
実行時間制限: 2 sec / メモリ制限: 1024 MB
配点 : 300点
問題文
a + b < c \sqrt a+\sqrt b <\sqrt c a+b<cですか?
制約
- 1 ≤ a , b , c ≤ 1 0 9 1\leq a,b,c \leq 10^9 1≤a,b,c≤109
- 入力は全て整数である。
入力
入力は以下の形式で標準入力から与えられる。
a b c
出力
a
+
b
<
c
\sqrt a+\sqrt b <\sqrt c
a+b<cならば'Yes'
、そうでないならば'No'
と出力せよ。
入力例 1
2 3 9
出力例 1
No
2 + 3 < 9 \sqrt 2+\sqrt 3 <\sqrt 9 2+3<9ではありません。
入力例 2
2 3 10
出力例 2
Yes
2 + 3 < 1 0 \sqrt 2+\sqrt 3 <\sqrt 10 2+3<10です。
解法
如果只是正常的情况下,我们只要使用sqrt函数然后对三个数求平方根,之后我们判断sqrt(a) + sqrt(b)和sqrt( c )的大小来输出结果。但是由于这题的数字可以非常大, (a, b, c) = (249999999, 250000000, 999999998) 这个例子就会WA。我们可以通过下面的代码进行实验。
printf("%.30f\n", sqrt(249999999));
printf("%.30f\n", sqrt(250000000));
printf("%.30f\n", sqrt(249999999) + sqrt(250000000));
printf("%.30f\n", sqrt(999999998));
实验结果如下
15811.388269219120047637261450290680
15811.388300841896125348284840583801
31622.776570061017991974949836730957
31622.776570061017991974949836730957
通过这个实验我们发现原本 2 49999999 + 2 50000000 < 9 99999998 \sqrt 249999999+\sqrt 250000000 <\sqrt 999999998 249999999+250000000<999999998应该成立的不等式,实验里却相等了,因此才出现了WA。
那么这里就有两种办法可以让程序AC。
1.由于a,b,c最大也就 1 0 9 10^9 109,我们首先可以使用精确度更高的long double,然后在不等式的前半式添加上一个非常小的ε 就可以了。代码如下
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
int main(void)
{
long double a, b, c;
cin >> a >> b >> c;
long double eps = 1.0E-14;
if (sqrt(a) + sqrt(b) + eps < sqrt(c)) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
但这种方法只要ε 设置太大或者太小都会有可能导致WC(根据test cases),所以我个人推荐下面一种方法
2.第二种方法一开始我想到了,我们用以下数式来表示
a
+
b
<
c
(1)
\sqrt a+\sqrt b <\sqrt c \tag{1}
a+b<c(1)
⇔
(
a
+
b
)
2
<
c
(2)
\Leftrightarrow(\sqrt a+\sqrt b)^2 <c \tag{2}
⇔(a+b)2<c(2)
⇔
2
a
b
<
c
−
a
−
b
(3)
\Leftrightarrow 2\sqrt ab<c-a-b \tag{3}
⇔2ab<c−a−b(3)
⇔
c
−
a
−
b
>
0
∩
4
a
b
<
(
c
−
a
−
b
)
2
(4)
\Leftrightarrow c-a-b>0 \cap 4ab < (c-a-b)^2 \tag{4}
⇔c−a−b>0∩4ab<(c−a−b)2(4)
但是我当时默认ab相乘的话会超出long long int的范围,但后来仔细想想其实并不会。。。因此我们就可以直接这样写代码了。
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
int main(void){
ll a,b,c;
cin >> a >> b >> c;
ll d = c - a - b;
if(d > 0 && d * d > 4 * a * b){
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}
D - String Equivalence
実行時間制限: 2 sec / メモリ制限: 1024 MB
配点 : 400点
問題文
この問題では、英小文字からなる文字列のみを考えます。
文字列 s,t は以下の条件を満たすとき 同型 であるといいます。
- ∣ s ∣ = ∣ t ∣ |s|=|t| ∣s∣=∣t∣である。
- 任意の
i
,
j
i,j
i,j に対し次のいずれかが成立する。
- s i = s j s_i=s_j si=sjかつ t i = t j t_i=t_j ti=tj
- s i ≠ s j s_i\neq s_j si=sjかつ t i ≠ t j t_i\neq t_j ti=tj
たとえば、abcac
と zyxzx
は同型ですが、abcac
と ppppp
は同型ではありません。
文字列
s
s
s は以下の条件を満たすとき標準形であるといいます。
- 任意の
s
s
sと同型な文字列
t
t
t に対し、
s
≤
t
s≤t
s≤tが成立する。ただしここで
≤
≤
≤は辞書順での比較を表す。たとえば、
abcac
は標準形ですが、zyxzx
はそれより辞書順で小さいabcac
と同型のため標準形ではありません。
整数 N が与えられます。 長さ Nの標準形の文字列を全て、辞書順で昇順で出力してください。
制約
- 1 ≤ N ≤ 10 1\leq N \leq 10 1≤N≤10
- 入力は全て整数である。
入力
入力は以下の形式で標準入力から与えられる。
N
出力
長さ N N Nの標準形の文字列が K K K個あり、辞書順で w 1 , . . . , w k w_1,...,w_k w1,...,wkであるとする。 このとき以下の形式で出力せ。
w
1
w_1
w1
.
.
w
K
w_K
wK
入力例 1
1
出力例 1
a
入力例 1
2
出力例 1
aa
ab
题解
这题就是非常明显的DFS问题了。
我们如果把文字列
s
=
s
1
s
2
.
.
.
s
N
s = s_1s_2 . . . s_N
s=s1s2...sN的标准型用公式表示的话:
- s 1 = a s_1=a s1=a
-
s
k
=
m
a
x
{
s
1
,
.
.
.
s
k
}
+
1
(
∀
2
≤
k
≤
N
)
s_k=max\{ s_1,...s_k\}+1(\forall2\leq k \leq N)
sk=max{s1,...sk}+1(∀2≤k≤N)
这里加一指的是根据字典顺序的下一个字母
代码如下:
#include <iostream>
#include <string>
using namespace std;
int N;
void dfs(string s, char mx){
if(s.length() == N){
printf("%s\n", s.c_str());
} else {
for(char c='a';c<=mx;c++){
dfs(s + c, ((c == mx) ? (char)(mx + 1) : mx));
}
}
}
int main(void){
cin >> N;
dfs("", 'a');
return 0;
}
E - Three Substrings
実行時間制限: 2 sec / メモリ制限: 1024 MB
配点 : 500点
問題文
すぬけ君は、文字列 s を持っています。 あぬけ君、ぶぬけ君、くぬけ君は次のような方法でそれぞれ文字列 a,b,c を得ました。
- sの空でない (s 全体であってもよい) 連続な部分文字列を一つ選ぶ。その部分文字列のうちいくつかの文字 (0 個や全部であってもよい) を
?
で置き換える。
たとえば、s が mississippi
であるとき、部分文字列として ssissip
を選び、その 1,3 文字目を ?
で置き換えることで ?s?ssip
を得ることができます。
文字列 a,b,c が与えられます。 s の長さとして考えられる最小値を求めてください。
制約
- 1 ≤ ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ 2000 1\leq |a| ,|b|,|c| \leq 2000 1≤∣a∣,∣b∣,∣c∣≤2000
-
a
,
b
,
c
a,b,c
a,b,c は英小文字と
?
からなる。
入力
入力は以下の形式で標準入力から与えられる。
a
b
c
出力
s の長さとして考えられる最小値を出力せよ。
入力例 1
a?c
der
cod
出力例 1
7
たとえば、s が atcoder
のとき条件を満たします。
入力例 2
atcoder
atcoder
???????
出力例 2
7
a,b,c は相異なるとは限りません。
解说
这题非常容易出错的地方就是考虑这个问题的时候默认了一些情况(例如从默认左边开始遍历)
我们举个例子
??c? → ac?a →?b?a (我们以这个为问题)
错误情况:
• ??c? (??c? を配置)
• acca (ac?a を重ねて配置)
• accab?a (?b?a をできる限り左に配置)
正确情况:
• ??c? (??c? を配置)
• ?ac?a (ac?a をわざと一つずらして配置)
• ?acbaa (?b?a をできる限り左に配置)
因此对于greedy算法必须要认真证明再使用,或者使用全探索以及DP之类的方法。
对于这一题,我们以a为中心,来全探索b,c相对应的位置。当我们把a的最左边当成0位置的时候,其他的文字列是可以从-4000移动到4000的,我们需要注意这一点。
代码如下:
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
vector<bool> check(string S, string T, int lim) {
vector<bool> res(2 * lim + 1, true);
for (int i = -lim; i <= lim; ++i) {
for (int j = 0; j < S.size(); ++j) {
int t = j - i;
if (0 <= t && t < int(T.size()) && S[j] != '?' && T[t] != '?' && S[j] != T[t]) {
res[i + lim] = false;
break;
}
}
}
return res;
}
int main() {
cin.tie(0);
ios_base::sync_with_stdio(false);
string A, B, C;
cin >> A >> B >> C;
int len = A.size() + B.size() + C.size();
vector<bool> resab = check(A, B, len);
vector<bool> resbc = check(B, C, len);
vector<bool> resac = check(A, C, len);
int ans = (1 << 30);
for (int i = -len; i <= len; ++i) {
if (!resab[i + len]) continue;
for (int j = -len; j <= len; ++j) {
if (!resbc[j + len]) continue;
int k = i + j;
if (-len <= k && k <= len && resac[k + len]) {
int sa = 0, sb = sa + i, sc = sb + j;
int pl = min({ sa, sb, sc });
int pr = max({ sa + int(A.size()), sb + int(B.size()), sc + int(C.size()) });
ans = min(ans, pr - pl);
}
}
}
cout << ans << endl;
return 0;
}